package main import ( "encoding/json" "testing" "git.iwork-ai.com/xdc/iqudo-top1/internal/model" "git.iwork-ai.com/xdc/iqudo-top1/internal/sop" "gorm.io/datatypes" ) func TestPetDoctorSOPPassesPublishValidation(t *testing.T) { fields := petFieldModels() knowledgeIDs := map[string]uint64{ "线上问诊边界": 1, "急症红旗征象": 2, "问诊用药安全原则": 3, "观察与复诊提示": 4, } nodes := petNodeModels(petNodes(knowledgeIDs)) edges := petEdgeModels(petEdges()) if len(fields) != 47 || len(nodes) != 21 || len(edges) != 21 { t.Fatalf("unexpected definition size: fields=%d nodes=%d edges=%d", len(fields), len(nodes), len(edges)) } problems := sop.ValidateForPublish("start", nodes, edges, sop.ValidationContext{ Fields: fields, PublishedKnowledgeCardIDs: map[uint64]bool{ 1: true, 2: true, 3: true, 4: true, }, }) if len(problems) > 0 { t.Fatalf("pet doctor SOP should be publishable, got: %v", problems) } } func TestPetDoctorSOPHasBothRiskEscalations(t *testing.T) { nodes := petNodes(map[string]uint64{ "线上问诊边界": 1, "急症红旗征象": 2, "问诊用药安全原则": 3, "观察与复诊提示": 4, }) escalations := map[string]bool{} for _, node := range nodes { if node.Type == "escalate" { escalations[node.Key] = true } } for _, key := range []string{"emergency_escalate", "medication_escalate"} { if !escalations[key] { t.Fatalf("missing risk escalation node %s", key) } } } func TestSameJSONIgnoresObjectOrderAndWhitespace(t *testing.T) { left := []byte(`{"standard_copy":"安全提示","risk_note":"立即就医"}`) right := []byte(`{ "risk_note": "立即就医", "standard_copy": "安全提示" }`) if !sameJSON(left, right) { t.Fatal("equivalent JSON objects should match") } } func petFieldModels() []model.ScenarioField { definitions := petFieldDefinitions() fields := make([]model.ScenarioField, 0, len(definitions)) for _, definition := range definitions { options, _ := json.Marshal(definition.Options) validation, _ := json.Marshal(definition.Validation) fields = append(fields, model.ScenarioField{ FieldKey: definition.Key, FieldName: definition.Name, FieldType: definition.Type, Required: definition.Required, Options: datatypes.JSON(options), Validation: datatypes.JSON(validation), }) } return fields } func petNodeModels(definitions []nodeDefinition) []model.SOPNode { nodes := make([]model.SOPNode, 0, len(definitions)) for _, definition := range definitions { config, _ := json.Marshal(definition.Config) nodes = append(nodes, model.SOPNode{ NodeKey: definition.Key, Type: definition.Type, Title: definition.Title, Content: definition.Content, Config: datatypes.JSON(config), }) } return nodes } func petEdgeModels(definitions []edgeDefinition) []model.SOPEdge { edges := make([]model.SOPEdge, 0, len(definitions)) for _, definition := range definitions { condition, _ := json.Marshal(definition.Condition) edges = append(edges, model.SOPEdge{ SourceNodeKey: definition.Source, TargetNodeKey: definition.Target, Condition: datatypes.JSON(condition), Priority: definition.Priority, }) } return edges }