package scenario import ( "strings" "testing" ) func TestValidateContractInput(t *testing.T) { valid := contractInput{ OutputSchema: map[string]interface{}{"fields": []interface{}{ map[string]interface{}{"key": "matched_topics", "source": "derived", "source_field": "topics"}, }}, ResultSchema: map[string]interface{}{"fields": []interface{}{ map[string]interface{}{"key": "selected_products", "name": "成交商品", "type": "array", "items": map[string]interface{}{"type": "string"}}, }}, Rules: []ruleInput{{ RuleKey: "match_topic", Name: "匹配主题", Condition: map[string]interface{}{"field": "product_tags", "operator": "contains", "value": "hot"}, Actions: []map[string]interface{}{{"operation": "append", "field": "topics", "value": "topic_hot"}}, }}, AllowedOrigins: []string{"https://crm.example.com"}, } if err := validateContractInput(valid); err != nil { t.Fatalf("valid contract rejected: %v", err) } tests := []struct { name string edit func(*contractInput) want string }{ {name: "duplicate output", edit: func(input *contractInput) { input.OutputSchema["fields"] = append(input.OutputSchema["fields"].([]interface{}), map[string]interface{}{"key": "matched_topics"}) }, want: "输出字段标识"}, {name: "invalid source", edit: func(input *contractInput) { input.OutputSchema["fields"].([]interface{})[0].(map[string]interface{})["source"] = "script" }, want: "不支持的来源"}, {name: "invalid condition", edit: func(input *contractInput) { input.Rules[0].Condition = map[string]interface{}{"all": []interface{}{}} }, want: "非空数组"}, {name: "invalid action", edit: func(input *contractInput) { input.Rules[0].Actions[0]["operation"] = "execute" }, want: "不支持的动作"}, {name: "invalid origin", edit: func(input *contractInput) { input.AllowedOrigins = []string{"crm.example.com"} }, want: "完整的 http/https Origin"}, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { input := cloneContractInput(valid) test.edit(&input) if err := validateContractInput(input); err == nil || !strings.Contains(err.Error(), test.want) { t.Fatalf("error = %v, want containing %q", err, test.want) } }) } } func cloneContractInput(input contractInput) contractInput { field := input.OutputSchema["fields"].([]interface{})[0].(map[string]interface{}) fieldCopy := map[string]interface{}{} for key, value := range field { fieldCopy[key] = value } rule := input.Rules[0] condition := map[string]interface{}{} for key, value := range rule.Condition { condition[key] = value } action := map[string]interface{}{} for key, value := range rule.Actions[0] { action[key] = value } rule.Condition = condition rule.Actions = []map[string]interface{}{action} return contractInput{OutputSchema: map[string]interface{}{"fields": []interface{}{fieldCopy}}, ResultSchema: input.ResultSchema, Rules: []ruleInput{rule}, AllowedOrigins: append([]string{}, input.AllowedOrigins...)} }