优化API测试参数处理,增加调试信息,更新复合案例和API测试参数结构

This commit is contained in:
longpeng 2025-06-30 10:54:24 +08:00
parent a2e209e530
commit 7c68cd9952
4 changed files with 82 additions and 34 deletions

View File

@ -42,6 +42,8 @@ func LoadCompositeCaseSteps(ctx context.Context, compositeCaseId string) ([]*pb.
// 转换数据库模型为 Protobuf 结构
var pbSteps []*pb.CompositeCaseStepDefinition
for _, step := range steps {
fmt.Printf("DEBUG: Processing step ID: %d, Type: %s, ApiTestParameters: %v\n",
step.ID, step.StepType, step.ApiTestParameters != nil)
// 序列化参数为JSON
parametersJson, err := serializeStepParameters(step)
if err != nil {
@ -89,13 +91,19 @@ func serializeStepParameters(step models.CompositeCaseStep) (string, error) {
switch step.StepType {
case "API_TEST":
if step.ApiTestParameters != nil {
// 转换API参数
// Debug: 打印API参数信息
fmt.Printf("DEBUG: API Parameters - Endpoint: '%s', Method: '%s', Body: '%s'\n",
step.ApiTestParameters.Endpoint,
step.ApiTestParameters.HttpMethod,
step.ApiTestParameters.Body)
// 转换API参数 - 只包含protobuf定义的字段
apiParams := map[string]interface{}{
"endpoint": step.ApiTestParameters.Endpoint,
"method": step.ApiTestParameters.HttpMethod,
"body": step.ApiTestParameters.Body,
"timeout": step.ApiTestParameters.Timeout,
"retry_count": step.ApiTestParameters.RetryCount,
"test_case_id": fmt.Sprintf("step_%d", step.ID),
"endpoint": step.ApiTestParameters.Endpoint,
"http_method": step.ApiTestParameters.HttpMethod,
"request_body": step.ApiTestParameters.Body,
"expected_status_code": 200, // 默认期望200状态码
}
// 解析headers和query_params JSON字符串
@ -114,6 +122,7 @@ func serializeStepParameters(step models.CompositeCaseStep) (string, error) {
params = apiParams
} else {
fmt.Printf("DEBUG: step.ApiTestParameters is nil for step ID: %d\n", step.ID)
params = map[string]interface{}{}
}

View File

@ -7,13 +7,18 @@ import (
// CompositeCase 复合案例定义
type CompositeCase struct {
ID uint `json:"id" gorm:"primaryKey"`
Name string `json:"name" gorm:"not null;size:255"`
Description string `json:"description" gorm:"type:text"`
Status string `json:"status" gorm:"default:'active';size:50"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
DeletedAt gorm.DeletedAt `json:"-" gorm:"index"`
ID uint `json:"id" gorm:"primaryKey"`
CreatedAt time.Time `json:"created_at"`
CreatedBy string `json:"created_by" gorm:"size:64;default:''"`
UpdatedAt time.Time `json:"updated_at"`
UpdatedBy string `json:"updated_by" gorm:"size:64;default:''"`
Version int16 `json:"version" gorm:"default:0"`
IsDel int8 `json:"is_del" gorm:"default:0"`
ExtJson string `json:"ext_json" gorm:"size:2048;default:''"`
Name string `json:"name" gorm:"not null;size:255"`
Description string `json:"description" gorm:"type:text"`
Status string `json:"status" gorm:"default:'active';size:50"`
DeletedAt *time.Time `json:"deleted_at"`
// 关联的步骤
Steps []CompositeCaseStep `json:"steps" gorm:"foreignKey:CompositeCaseID"`
@ -22,6 +27,13 @@ type CompositeCase struct {
// ApiTestParameters API测试参数
type ApiTestParameters struct {
ID uint `json:"id" gorm:"primaryKey"`
CreatedAt time.Time `json:"created_at"`
CreatedBy string `json:"created_by" gorm:"size:64;default:''"`
UpdatedAt time.Time `json:"updated_at"`
UpdatedBy string `json:"updated_by" gorm:"size:64;default:''"`
Version int16 `json:"version" gorm:"default:0"`
IsDel int8 `json:"is_del" gorm:"default:0"`
ExtJson string `json:"ext_json" gorm:"size:2048;default:''"`
StepID uint `json:"step_id" gorm:"not null"`
Endpoint string `json:"endpoint" gorm:"not null;size:512"`
HttpMethod string `json:"http_method" gorm:"not null;size:10;default:'GET'"`
@ -30,8 +42,6 @@ type ApiTestParameters struct {
Body string `json:"body" gorm:"type:text"`
Timeout int `json:"timeout" gorm:"default:30"`
RetryCount int `json:"retry_count" gorm:"default:0"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
// BeforeCreate GORM hook to ensure JSON fields are never empty
@ -59,6 +69,13 @@ func (params *ApiTestParameters) BeforeUpdate(tx *gorm.DB) error {
// UiTestParameters UI测试参数
type UiTestParameters struct {
ID uint `json:"id" gorm:"primaryKey"`
CreatedAt time.Time `json:"created_at"`
CreatedBy string `json:"created_by" gorm:"size:64;default:''"`
UpdatedAt time.Time `json:"updated_at"`
UpdatedBy string `json:"updated_by" gorm:"size:64;default:''"`
Version int16 `json:"version" gorm:"default:0"`
IsDel int8 `json:"is_del" gorm:"default:0"`
ExtJson string `json:"ext_json" gorm:"size:2048;default:''"`
StepID uint `json:"step_id" gorm:"not null"`
Selector string `json:"selector" gorm:"not null;size:512"`
SelectorType string `json:"selector_type" gorm:"default:'css';size:20"`
@ -70,8 +87,6 @@ type UiTestParameters struct {
ScreenshotName string `json:"screenshot_name" gorm:"size:255"`
AssertionType string `json:"assertion_type" gorm:"size:50"`
AssertionValue string `json:"assertion_value" gorm:"type:text"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
// ApiTestParametersRequest API测试参数请求
@ -102,18 +117,23 @@ type UiTestParametersRequest struct {
// CompositeCaseStep 复合案例步骤
type CompositeCaseStep struct {
ID uint `json:"id" gorm:"primaryKey"`
CreatedAt time.Time `json:"created_at"`
CreatedBy string `json:"created_by" gorm:"size:64;default:''"`
UpdatedAt time.Time `json:"updated_at"`
UpdatedBy string `json:"updated_by" gorm:"size:64;default:''"`
Version int16 `json:"version" gorm:"default:0"`
IsDel int8 `json:"is_del" gorm:"default:0"`
ExtJson string `json:"ext_json" gorm:"size:2048;default:''"`
CompositeCaseID uint `json:"composite_case_id" gorm:"not null"`
StepName string `json:"step_name" gorm:"not null;size:128"`
StepOrder int `json:"step_order" gorm:"not null"`
StepName string `json:"step_name" gorm:"not null;size:255"`
StepDescription string `json:"step_description" gorm:"type:text"`
StepType string `json:"step_type" gorm:"not null;size:100"`
ActivityName string `json:"activity_name" gorm:"not null;size:255"`
StepType string `json:"step_type" gorm:"not null;size:64"`
ActivityName string `json:"activity_name" gorm:"not null;size:128"`
IsRequired bool `json:"is_required" gorm:"default:true"`
StepDescription string `json:"step_description" gorm:"type:text"`
SuccessNextStepOrder *int `json:"success_next_step_order" gorm:"column:success_next_step_order"`
FailureNextStepOrder *int `json:"failure_next_step_order" gorm:"column:failure_next_step_order"`
RunCondition string `json:"run_condition" gorm:"type:json"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
// 关联的参数表
ApiTestParameters *ApiTestParameters `json:"api_test_parameters,omitempty" gorm:"foreignKey:StepID"`

View File

@ -26,9 +26,13 @@ func CreateCompositeCase(tx *gorm.DB, compositeCase *models.CompositeCase) error
func GetCompositeCaseByID(id uint) (*models.CompositeCase, error) {
var compositeCase models.CompositeCase
err := DB.Preload("Steps.ApiTestParameters").Preload("Steps.UiTestParameters").Preload("Steps", func(db *gorm.DB) *gorm.DB {
return db.Order("step_order ASC")
}).First(&compositeCase, id).Error
err := DB.Preload("Steps.ApiTestParameters", "is_del = 0").
Preload("Steps.UiTestParameters", "is_del = 0").
Preload("Steps", func(db *gorm.DB) *gorm.DB {
return db.Where("is_del = 0").Order("step_order ASC")
}).
Where("is_del = 0").
First(&compositeCase, id).Error
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
@ -67,7 +71,7 @@ func ListCompositeCases(page, pageSize int, status string) ([]models.CompositeCa
var compositeCases []models.CompositeCase
var total int64
query := DB.Model(&models.CompositeCase{})
query := DB.Model(&models.CompositeCase{}).Where("is_del = 0")
if status != "" {
query = query.Where("status = ?", status)
@ -80,9 +84,12 @@ func ListCompositeCases(page, pageSize int, status string) ([]models.CompositeCa
// 分页查询
offset := (page - 1) * pageSize
err := query.Preload("Steps.ApiTestParameters").Preload("Steps.UiTestParameters").Preload("Steps", func(db *gorm.DB) *gorm.DB {
return db.Order("step_order ASC")
}).Offset(offset).Limit(pageSize).Order("created_at DESC").Find(&compositeCases).Error
err := query.Preload("Steps.ApiTestParameters", "is_del = 0").
Preload("Steps.UiTestParameters", "is_del = 0").
Preload("Steps", func(db *gorm.DB) *gorm.DB {
return db.Where("is_del = 0").Order("step_order ASC")
}).
Offset(offset).Limit(pageSize).Order("created_at DESC").Find(&compositeCases).Error
if err != nil {
return nil, 0, fmt.Errorf("获取复合案例列表失败: %w", err)
@ -98,7 +105,7 @@ func ExistsCompositeCase(tx *gorm.DB, id uint) (*models.CompositeCase, error) {
}
var compositeCase models.CompositeCase
err := tx.First(&compositeCase, id).Error
err := tx.Where("is_del = 0").First(&compositeCase, id).Error
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return nil, fmt.Errorf("复合案例不存在")
@ -132,7 +139,10 @@ func GetCompositeCaseSteps(compositeCaseId string) ([]models.CompositeCaseStep,
return nil, fmt.Errorf("invalid composite case id: %w", err)
}
err = DB.Preload("ApiTestParameters").Preload("UiTestParameters").Where("composite_case_id = ?", uint(id)).
// 查询步骤时,预加载参数表并过滤未删除的记录
err = DB.Preload("ApiTestParameters", "is_del = 0").
Preload("UiTestParameters", "is_del = 0").
Where("composite_case_id = ? AND is_del = 0", uint(id)).
Order("step_order ASC").
Find(&steps).Error

View File

@ -3,6 +3,7 @@ package workflows
import (
"encoding/json"
"go.temporal.io/sdk/temporal"
"go.uber.org/zap"
"sort"
"strconv"
"time"
@ -109,6 +110,7 @@ func DynamicTestSuiteWorkflow(ctx workflow.Context, input *pb.DynamicTestRunInpu
var activityInput interface{} // 存储特定 Activity 的输入参数(类型由 activity_name 决定)
var activityResult interface{} // 存储特定 Activity 的输出结果(类型由 activity_name 决定)
zap.L().Debug("ParametersJson", zap.Any("ParametersJson", step.ParametersJson))
// 使用参数处理器解析参数模板,替换其中的变量引用
processedParametersJson, err := parameterProcessor.ProcessTemplate(step.ParametersJson)
if err != nil {
@ -116,6 +118,7 @@ func DynamicTestSuiteWorkflow(ctx workflow.Context, input *pb.DynamicTestRunInpu
overallSuccess = false
break
}
zap.L().Debug("parameterProcessor.ProcessTemplate", zap.Any("processedParametersJson", processedParametersJson))
// 验证模板中的变量引用是否有效
if isValid, errors := parameterProcessor.ValidateTemplate(step.ParametersJson); !isValid {
@ -131,11 +134,13 @@ func DynamicTestSuiteWorkflow(ctx workflow.Context, input *pb.DynamicTestRunInpu
apiReq := &pb.ApiTestRequest{}
// 1. 首先验证JSON格式
if !json.Valid([]byte(processedParametersJson)) {
logger.Error("Invalid JSON format in parameters")
logger.Error("Invalid JSON format in parameters", "json", processedParametersJson)
overallSuccess = false
break
}
logger.Info("Processing API test parameters", "json", processedParametersJson)
// 2. 解析JSON时增加错误详情
if err := json.Unmarshal([]byte(processedParametersJson), apiReq); err != nil {
logger.Error("Failed to unmarshal API test parameters",
@ -145,9 +150,13 @@ func DynamicTestSuiteWorkflow(ctx workflow.Context, input *pb.DynamicTestRunInpu
break
}
logger.Info("Parsed API request", "endpoint", apiReq.Endpoint, "method", apiReq.HttpMethod)
// 3. 验证必要字段
if apiReq.Endpoint == "" || apiReq.HttpMethod == "" {
logger.Error("Missing required fields in API test parameters")
logger.Error("Missing required fields in API test parameters",
"endpoint", apiReq.Endpoint,
"http_method", apiReq.HttpMethod)
overallSuccess = false
break
}