package playbook import ( "strings" "testing" ) const testText1 = ` //comment for the action !!mailclient.configure host:localhost name: 'myname' port:25 secure: 1 reset:1 description:' a description can be multiline like this ' ` func TestParse(t *testing.T) { pb, err := NewFromText(testText1) if err != nil { t.Fatalf("Failed to parse text: %v", err) } if len(pb.Actions) != 1 { t.Errorf("Expected 1 action, got %d", len(pb.Actions)) } action := pb.Actions[0] if action.Actor != "mailclient" { t.Errorf("Expected actor 'mailclient', got '%s'", action.Actor) } if action.Name != "configure" { t.Errorf("Expected name 'configure', got '%s'", action.Name) } if action.Comments != "comment for the action" { t.Errorf("Expected comment 'comment for the action', got '%s'", action.Comments) } // Test params name := action.Params.Get("name") if name != "myname" { t.Errorf("Expected name 'myname', got '%s'", name) } host := action.Params.Get("host") if host != "localhost" { t.Errorf("Expected host 'localhost', got '%s'", host) } port, err := action.Params.GetInt("port") if err != nil || port != 25 { t.Errorf("Expected port 25, got %d, error: %v", port, err) } secure := action.Params.GetBool("secure") if !secure { t.Errorf("Expected secure to be true, got false") } reset := action.Params.GetBool("reset") if !reset { t.Errorf("Expected reset to be true, got false") } // Test multiline description desc := action.Params.Get("description") // Just check that the description contains the expected text if !strings.Contains(desc, "a description can be multiline") || !strings.Contains(desc, "like this") { t.Errorf("Description doesn't contain expected content: '%s'", desc) } } func TestHeroScript(t *testing.T) { pb, err := NewFromText(testText1) if err != nil { t.Fatalf("Failed to parse text: %v", err) } // Generate heroscript script := pb.HeroScript(true) // Parse the generated script again pb2, err := NewFromText(script) if err != nil { t.Fatalf("Failed to parse generated script: %v", err) } // Verify the actions are the same if len(pb2.Actions) != len(pb.Actions) { t.Errorf("Expected %d actions, got %d", len(pb.Actions), len(pb2.Actions)) } // Verify the actions have the same actor and name if pb.Actions[0].Actor != pb2.Actions[0].Actor || pb.Actions[0].Name != pb2.Actions[0].Name { t.Errorf("Actions don't match: %s.%s vs %s.%s", pb.Actions[0].Actor, pb.Actions[0].Name, pb2.Actions[0].Actor, pb2.Actions[0].Name) } // Verify the parameters are the same params1 := pb.Actions[0].Params.GetAll() params2 := pb2.Actions[0].Params.GetAll() // Check that all keys in params1 exist in params2 for k, v1 := range params1 { v2, exists := params2[k] if !exists { t.Errorf("Key %s missing in generated script", k) continue } // For multiline strings, just check that they contain the same content if strings.Contains(v1, "\n") { if !strings.Contains(v2, "description") || !strings.Contains(v2, "multiline") { t.Errorf("Multiline value for key %s doesn't match: '%s' vs '%s'", k, v1, v2) } } else if v1 != v2 { t.Errorf("Value for key %s doesn't match: '%s' vs '%s'", k, v1, v2) } } } func TestSpacedValues(t *testing.T) { const spacedValuesText = ` !!mailclient.configure name: 'myname' host: 'localhost' port: 25 secure: 1 description: 'This is a description' ` pb, err := NewFromText(spacedValuesText) if err != nil { t.Fatalf("Failed to parse text with spaces between colon and quoted values: %v", err) } if len(pb.Actions) != 1 { t.Errorf("Expected 1 action, got %d", len(pb.Actions)) } action := pb.Actions[0] if action.Actor != "mailclient" || action.Name != "configure" { t.Errorf("Action incorrect: %s.%s", action.Actor, action.Name) } // Test params with spaces after colon name := action.Params.Get("name") if name != "myname" { t.Errorf("Expected name 'myname', got '%s'", name) } host := action.Params.Get("host") if host != "localhost" { t.Errorf("Expected host 'localhost', got '%s'", host) } desc := action.Params.Get("description") if desc != "This is a description" { t.Errorf("Expected description 'This is a description', got '%s'", desc) } } func TestMultipleActions(t *testing.T) { const multipleActionsText = ` !!mailclient.configure name:'myname' host:'localhost' !!system.update force:1 packages:'git,curl,wget' ` pb, err := NewFromText(multipleActionsText) if err != nil { t.Fatalf("Failed to parse text: %v", err) } if len(pb.Actions) != 2 { t.Errorf("Expected 2 actions, got %d", len(pb.Actions)) } // Check first action action1 := pb.Actions[0] if action1.Actor != "mailclient" || action1.Name != "configure" { t.Errorf("First action incorrect: %s.%s", action1.Actor, action1.Name) } // Check second action action2 := pb.Actions[1] if action2.Actor != "system" || action2.Name != "update" { t.Errorf("Second action incorrect: %s.%s", action2.Actor, action2.Name) } force := action2.Params.GetBool("force") if !force { t.Errorf("Expected force to be true, got false") } packages := action2.Params.Get("packages") if packages != "git,curl,wget" { t.Errorf("Expected packages 'git,curl,wget', got '%s'", packages) } }