| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652 |
- 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()
|