package cmd import ( "context" "encoding/json" "os" "testing" "github.com/urfave/cli/v3" ) // TestWorkflowCommandStructure tests that the workflow command is properly structured func TestWorkflowCommandStructure(t *testing.T) { cmd := WorkflowCommand() if cmd.Name != "workflow" { t.Errorf("Expected command name 'workflow', got '%s'", cmd.Name) } if cmd.Usage == "" { t.Error("Command usage should not be empty") } // Check subcommands exist expectedSubcmds := []string{"template", "instance", "node"} subcmdMap := make(map[string]bool) for _, subcmd := range cmd.Commands { subcmdMap[subcmd.Name] = true } for _, expected := range expectedSubcmds { if !subcmdMap[expected] { t.Errorf("Missing expected subcommand: %s", expected) } } } // TestWorkflowTemplateSubcommands tests the template subcommand structure func TestWorkflowTemplateSubcommands(t *testing.T) { cmd := WorkflowCommand() var templateCmd *cli.Command for _, c := range cmd.Commands { if c.Name == "template" { templateCmd = c break } } if templateCmd == nil { t.Fatal("template subcommand not found") } expectedActions := []string{"list", "get", "create", "update", "delete"} actionMap := make(map[string]bool) for _, action := range templateCmd.Commands { actionMap[action.Name] = true } for _, expected := range expectedActions { if !actionMap[expected] { t.Errorf("Missing template action: %s", expected) } } } // TestWorkflowInstanceSubcommands tests the instance subcommand structure func TestWorkflowInstanceSubcommands(t *testing.T) { cmd := WorkflowCommand() var instanceCmd *cli.Command for _, c := range cmd.Commands { if c.Name == "instance" { instanceCmd = c break } } if instanceCmd == nil { t.Fatal("instance subcommand not found") } expectedActions := []string{"list", "get", "start", "cancel"} actionMap := make(map[string]bool) for _, action := range instanceCmd.Commands { actionMap[action.Name] = true } for _, expected := range expectedActions { if !actionMap[expected] { t.Errorf("Missing instance action: %s", expected) } } } // TestWorkflowNodeSubcommands tests the node subcommand structure func TestWorkflowNodeSubcommands(t *testing.T) { cmd := WorkflowCommand() var nodeCmd *cli.Command for _, c := range cmd.Commands { if c.Name == "node" { nodeCmd = c break } } if nodeCmd == nil { t.Fatal("node subcommand not found") } expectedActions := []string{"list", "get", "retry"} actionMap := make(map[string]bool) for _, action := range nodeCmd.Commands { actionMap[action.Name] = true } for _, expected := range expectedActions { if !actionMap[expected] { t.Errorf("Missing node action: %s", expected) } } } // TestWorkflowTemplateFlags tests that template command flags are properly defined func TestWorkflowTemplateFlags(t *testing.T) { cmd := WorkflowCommand() var templateCmd *cli.Command for _, c := range cmd.Commands { if c.Name == "template" { templateCmd = c break } } // Test create command flags var createCmd *cli.Command for _, c := range templateCmd.Commands { if c.Name == "create" { createCmd = c break } } flagNames := make(map[string]bool) for _, flag := range createCmd.Flags { flagNames[flag.Names()[0]] = true } expectedFlags := []string{"name", "description", "definition", "active"} for _, expected := range expectedFlags { if !flagNames[expected] { t.Errorf("Missing create flag: %s", expected) } } // Test that id flag is required for get command var getCmd *cli.Command for _, c := range templateCmd.Commands { if c.Name == "get" { getCmd = c break } } var idFlag *cli.StringFlag for _, flag := range getCmd.Flags { if flag.Names()[0] == "id" { idFlag = flag.(*cli.StringFlag) break } } if idFlag == nil { t.Error("Missing id flag on get command") } else if !idFlag.Required { t.Error("id flag should be required on get command") } } // TestWorkflowInstanceFlags tests that instance command flags are properly defined func TestWorkflowInstanceFlags(t *testing.T) { cmd := WorkflowCommand() var instanceCmd *cli.Command for _, c := range cmd.Commands { if c.Name == "instance" { instanceCmd = c break } } // Test start command flags var startCmd *cli.Command for _, c := range instanceCmd.Commands { if c.Name == "start" { startCmd = c break } } flagNames := make(map[string]bool) for _, flag := range startCmd.Flags { flagNames[flag.Names()[0]] = true } expectedFlags := []string{"template", "service", "context"} for _, expected := range expectedFlags { if !flagNames[expected] { t.Errorf("Missing start flag: %s", expected) } } // Test that template flag is required var templateFlag *cli.StringFlag for _, flag := range startCmd.Flags { if flag.Names()[0] == "template" { templateFlag = flag.(*cli.StringFlag) break } } if templateFlag == nil { t.Error("Missing template flag on start command") } else if !templateFlag.Required { t.Error("template flag should be required on start command") } } // TestWorkflowNodeFlags tests that node command flags are properly defined func TestWorkflowNodeFlags(t *testing.T) { cmd := WorkflowCommand() var nodeCmd *cli.Command for _, c := range cmd.Commands { if c.Name == "node" { nodeCmd = c break } } // Test list command flags var listCmd *cli.Command for _, c := range nodeCmd.Commands { if c.Name == "list" { listCmd = c break } } var instanceFlag *cli.StringFlag for _, flag := range listCmd.Flags { if flag.Names()[0] == "instance" { instanceFlag = flag.(*cli.StringFlag) break } } if instanceFlag == nil { t.Error("Missing instance flag on node list command") } else if !instanceFlag.Required { t.Error("instance flag should be required on node list command") } } // TestReadFileOrString tests the helper function for reading files func TestReadFileOrString(t *testing.T) { // Test with regular string (no @ prefix) input := "hello world" result := ReadFileOrString(input) if result != input { t.Errorf("Expected '%s', got '%s'", input, result) } // Test with non-existent file (should return original) input = "@nonexistent_file.json" result = ReadFileOrString(input) if result != input { t.Errorf("Expected original input for non-existent file, got '%s'", result) } // Test with actual file tmpFile, err := os.CreateTemp("", "test_*.json") if err != nil { t.Fatalf("Failed to create temp file: %v", err) } defer os.Remove(tmpFile.Name()) testContent := `{"test": "content"}` if _, err := tmpFile.WriteString(testContent); err != nil { t.Fatalf("Failed to write to temp file: %v", err) } tmpFile.Close() result = ReadFileOrString("@" + tmpFile.Name()) if result != testContent { t.Errorf("Expected file content '%s', got '%s'", testContent, result) } } // TestWorkflowTemplateJSON tests JSON marshaling of WorkflowTemplate func TestWorkflowTemplateJSON(t *testing.T) { template := WorkflowTemplate{ ID: "1", Name: "Test Template", Description: "A test template", Definition: `{"nodes": []}`, IsActive: true, CreatedBy: &User{ID: "1", Email: "test@example.com"}, CreatedAt: "2024-01-01T00:00:00Z", UpdatedAt: "2024-01-01T00:00:00Z", } data, err := json.Marshal(template) if err != nil { t.Fatalf("Failed to marshal WorkflowTemplate: %v", err) } var unmarshaled WorkflowTemplate if err := json.Unmarshal(data, &unmarshaled); err != nil { t.Fatalf("Failed to unmarshal WorkflowTemplate: %v", err) } if unmarshaled.ID != template.ID { t.Errorf("Expected ID '%s', got '%s'", template.ID, unmarshaled.ID) } if unmarshaled.Name != template.Name { t.Errorf("Expected Name '%s', got '%s'", template.Name, unmarshaled.Name) } if unmarshaled.IsActive != template.IsActive { t.Errorf("Expected IsActive %v, got %v", template.IsActive, unmarshaled.IsActive) } } // TestWorkflowInstanceJSON tests JSON marshaling of WorkflowInstance func TestWorkflowInstanceJSON(t *testing.T) { completedAt := "2024-01-02T00:00:00Z" instance := WorkflowInstance{ ID: "1", Template: &WorkflowTemplate{ID: "1", Name: "Test"}, Status: "running", Context: `{"key": "value"}`, Service: &Service{ID: "1", Name: "Test Service"}, CreatedAt: "2024-01-01T00:00:00Z", UpdatedAt: "2024-01-01T00:00:00Z", CompletedAt: &completedAt, } data, err := json.Marshal(instance) if err != nil { t.Fatalf("Failed to marshal WorkflowInstance: %v", err) } var unmarshaled WorkflowInstance if err := json.Unmarshal(data, &unmarshaled); err != nil { t.Fatalf("Failed to unmarshal WorkflowInstance: %v", err) } if unmarshaled.ID != instance.ID { t.Errorf("Expected ID '%s', got '%s'", instance.ID, unmarshaled.ID) } if unmarshaled.Status != instance.Status { t.Errorf("Expected Status '%s', got '%s'", instance.Status, unmarshaled.Status) } if unmarshaled.CompletedAt == nil || *unmarshaled.CompletedAt != completedAt { t.Errorf("Expected CompletedAt '%s', got '%v'", completedAt, unmarshaled.CompletedAt) } } // TestWorkflowNodeJSON tests JSON marshaling of WorkflowNode func TestWorkflowNodeJSON(t *testing.T) { startedAt := "2024-01-01T01:00:00Z" node := WorkflowNode{ ID: "1", NodeKey: "node_1", NodeType: "task", Status: "running", Task: &Task{ID: "1", Title: "Test Task"}, InputData: `{"input": "data"}`, OutputData: `{"output": "data"}`, RetryCount: 2, CreatedAt: "2024-01-01T00:00:00Z", UpdatedAt: "2024-01-01T00:00:00Z", StartedAt: &startedAt, CompletedAt: nil, } data, err := json.Marshal(node) if err != nil { t.Fatalf("Failed to marshal WorkflowNode: %v", err) } var unmarshaled WorkflowNode if err := json.Unmarshal(data, &unmarshaled); err != nil { t.Fatalf("Failed to unmarshal WorkflowNode: %v", err) } if unmarshaled.ID != node.ID { t.Errorf("Expected ID '%s', got '%s'", node.ID, unmarshaled.ID) } if unmarshaled.NodeKey != node.NodeKey { t.Errorf("Expected NodeKey '%s', got '%s'", node.NodeKey, unmarshaled.NodeKey) } if unmarshaled.RetryCount != node.RetryCount { t.Errorf("Expected RetryCount %d, got %d", node.RetryCount, unmarshaled.RetryCount) } } // TestWorkflowTemplateListJSONFlag tests the JSON flag on template list func TestWorkflowTemplateListJSONFlag(t *testing.T) { cmd := WorkflowCommand() var templateCmd *cli.Command for _, c := range cmd.Commands { if c.Name == "template" { templateCmd = c break } } var listCmd *cli.Command for _, c := range templateCmd.Commands { if c.Name == "list" { listCmd = c break } } var jsonFlag *cli.BoolFlag for _, flag := range listCmd.Flags { if flag.Names()[0] == "json" { jsonFlag = flag.(*cli.BoolFlag) break } } if jsonFlag == nil { t.Error("Missing json flag on template list command") } } // TestWorkflowCommandInRoot tests that WorkflowCommand is registered in RootCommand func TestWorkflowCommandInRoot(t *testing.T) { rootCmd := RootCommand() found := false for _, cmd := range rootCmd.Commands { if cmd.Name == "workflow" { found = true break } } if !found { t.Error("WorkflowCommand not found in RootCommand") } } // TestWorkflowCommandHelp tests that help text is available func TestWorkflowCommandHelp(t *testing.T) { cmd := WorkflowCommand() if cmd.Description == "" { t.Error("WorkflowCommand should have a description") } // Test that subcommands have descriptions for _, subcmd := range cmd.Commands { if subcmd.Usage == "" { t.Errorf("Subcommand '%s' should have usage text", subcmd.Name) } } } // BenchmarkWorkflowCommand benchmarks the command creation func BenchmarkWorkflowCommand(b *testing.B) { for i := 0; i < b.N; i++ { _ = WorkflowCommand() } } // TestWorkflowTemplateRequiredFlags tests that required flags are enforced func TestWorkflowTemplateRequiredFlags(t *testing.T) { tests := []struct { name string action string shouldHave []string }{ {"get requires id", "get", []string{"id"}}, {"delete requires id", "delete", []string{"id"}}, {"update requires id", "update", []string{"id"}}, } cmd := WorkflowCommand() var templateCmd *cli.Command for _, c := range cmd.Commands { if c.Name == "template" { templateCmd = c break } } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { var actionCmd *cli.Command for _, c := range templateCmd.Commands { if c.Name == tt.action { actionCmd = c break } } if actionCmd == nil { t.Fatalf("Action '%s' not found", tt.action) } for _, requiredFlag := range tt.shouldHave { found := false for _, flag := range actionCmd.Flags { if flag.Names()[0] == requiredFlag { if strFlag, ok := flag.(*cli.StringFlag); ok { if strFlag.Required { found = true } } break } } if !found { t.Errorf("Flag '%s' should be required for action '%s'", requiredFlag, tt.action) } } }) } } // TestWorkflowInstanceRequiredFlags tests that required flags are enforced func TestWorkflowInstanceRequiredFlags(t *testing.T) { tests := []struct { name string action string shouldHave []string }{ {"get requires id", "get", []string{"id"}}, {"start requires template", "start", []string{"template"}}, {"cancel requires id", "cancel", []string{"id"}}, } cmd := WorkflowCommand() var instanceCmd *cli.Command for _, c := range cmd.Commands { if c.Name == "instance" { instanceCmd = c break } } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { var actionCmd *cli.Command for _, c := range instanceCmd.Commands { if c.Name == tt.action { actionCmd = c break } } if actionCmd == nil { t.Fatalf("Action '%s' not found", tt.action) } for _, requiredFlag := range tt.shouldHave { found := false for _, flag := range actionCmd.Flags { if flag.Names()[0] == requiredFlag { if strFlag, ok := flag.(*cli.StringFlag); ok { if strFlag.Required { found = true } } break } } if !found { t.Errorf("Flag '%s' should be required for action '%s'", requiredFlag, tt.action) } } }) } } // TestWorkflowNodeRequiredFlags tests that required flags are enforced func TestWorkflowNodeRequiredFlags(t *testing.T) { tests := []struct { name string action string shouldHave []string }{ {"list requires instance", "list", []string{"instance"}}, {"get requires id", "get", []string{"id"}}, {"retry requires id", "retry", []string{"id"}}, } cmd := WorkflowCommand() var nodeCmd *cli.Command for _, c := range cmd.Commands { if c.Name == "node" { nodeCmd = c break } } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { var actionCmd *cli.Command for _, c := range nodeCmd.Commands { if c.Name == tt.action { actionCmd = c break } } if actionCmd == nil { t.Fatalf("Action '%s' not found", tt.action) } for _, requiredFlag := range tt.shouldHave { found := false for _, flag := range actionCmd.Flags { if flag.Names()[0] == requiredFlag { if strFlag, ok := flag.(*cli.StringFlag); ok { if strFlag.Required { found = true } } break } } if !found { t.Errorf("Flag '%s' should be required for action '%s'", requiredFlag, tt.action) } } }) } } // Mock context for testing command actions var _ = context.Background()