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() nodes := petNodeModels(petNodes()) edges := petEdgeModels(petEdges()) if len(fields) != 13 || len(nodes) != 5 || len(edges) != 4 { 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, KnowledgeItems: []model.KnowledgeItem{ {Base: model.Base{ID: 1}, ItemKey: "consultation_boundary", Type: "guidance", Status: "active"}, {Base: model.Base{ID: 2}, ItemKey: "emergency_guidance", Type: "guidance", Status: "active"}, {Base: model.Base{ID: 3}, ItemKey: "medication_safety", Type: "guidance", Status: "active"}, {Base: model.Base{ID: 4}, ItemKey: "safety_net", Type: "guidance", Status: "active"}, {Base: model.Base{ID: 5}, ItemKey: "soft_stool", Type: "symptom", Status: "active"}, {Base: model.Base{ID: 6}, ItemKey: "poor_appetite", Type: "symptom", Status: "active"}, {Base: model.Base{ID: 7}, ItemKey: "ask_stool", Type: "copy", Status: "active"}, {Base: model.Base{ID: 8}, ItemKey: "gi_discomfort", Type: "disease", Status: "active"}, {Base: model.Base{ID: 9}, ItemKey: "gut_plan", Type: "plan", Status: "active"}, {Base: model.Base{ID: 10}, ItemKey: "gut_product", Type: "product", Status: "active"}, }, KnowledgeRelations: []model.KnowledgeRelation{ {FromKnowledgeID: 5, ToKnowledgeID: 7, RelationType: "recommended_copy"}, {FromKnowledgeID: 5, ToKnowledgeID: 8, RelationType: "possible_disease"}, {FromKnowledgeID: 5, ToKnowledgeID: 9, RelationType: "recommended_plan"}, {FromKnowledgeID: 8, ToKnowledgeID: 7, RelationType: "recommended_copy"}, {FromKnowledgeID: 8, ToKnowledgeID: 9, RelationType: "recommended_plan"}, {FromKnowledgeID: 8, ToKnowledgeID: 10, RelationType: "recommended_product"}, }, }) if len(problems) > 0 { t.Fatalf("pet doctor SOP should be publishable, got: %v", problems) } } func TestPetDoctorSOPIsFiveStepLinearFlow(t *testing.T) { wantNodes := []string{"start", "pet_info", "symptom", "recommend_product", "finish"} for index, node := range petNodes() { if node.Key != wantNodes[index] { t.Fatalf("node %d = %s, want %s", index, node.Key, wantNodes[index]) } } wantEdges := [][2]string{{"start", "pet_info"}, {"pet_info", "symptom"}, {"symptom", "recommend_product"}, {"recommend_product", "finish"}} for index, edge := range petEdges() { if edge.Source != wantEdges[index][0] || edge.Target != wantEdges[index][1] { t.Fatalf("edge %d = %s -> %s", index, edge.Source, edge.Target) } } } func TestPetKnowledgeKeyIsStableAndTypeScoped(t *testing.T) { if petKnowledgeKey("symptom", "腹泻") != petKnowledgeKey("symptom", " 腹泻 ") { t.Fatal("key should ignore surrounding whitespace") } if petKnowledgeKey("symptom", "腹泻") == petKnowledgeKey("disease", "腹泻") { t.Fatal("key should include knowledge type") } } 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, SourcePath: definition.SourcePath, 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 }