...
This commit is contained in:
212
_pkg2_dont_use/heroscript/playbook/parser.go
Normal file
212
_pkg2_dont_use/heroscript/playbook/parser.go
Normal file
@@ -0,0 +1,212 @@
|
||||
package playbook
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"git.ourworld.tf/herocode/heroagent/pkg/heroscript/paramsparser"
|
||||
"git.ourworld.tf/herocode/heroagent/pkg/tools"
|
||||
)
|
||||
|
||||
// State represents the parser state
|
||||
type State int
|
||||
|
||||
const (
|
||||
StateStart State = iota
|
||||
StateCommentForActionMaybe
|
||||
StateAction
|
||||
StateOtherText
|
||||
)
|
||||
|
||||
// PlayBookOptions contains options for creating a new PlayBook
|
||||
type PlayBookOptions struct {
|
||||
Text string
|
||||
Path string
|
||||
GitURL string
|
||||
GitPull bool
|
||||
GitBranch string
|
||||
GitReset bool
|
||||
Priority int
|
||||
}
|
||||
|
||||
// AddText adds heroscript text to the playbook
|
||||
func (p *PlayBook) AddText(text string, priority int) error {
|
||||
// Normalize text
|
||||
text = strings.ReplaceAll(text, "\t", " ")
|
||||
|
||||
var state State = StateStart
|
||||
var action *Action
|
||||
var comments []string
|
||||
var paramsData []string
|
||||
|
||||
// Process each line
|
||||
lines := strings.Split(text, "\n")
|
||||
for _, line := range lines {
|
||||
lineStrip := strings.TrimSpace(line)
|
||||
|
||||
if lineStrip == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
// Handle action state
|
||||
if state == StateAction {
|
||||
if !strings.HasPrefix(line, " ") || lineStrip == "" || strings.HasPrefix(lineStrip, "!") {
|
||||
state = StateStart
|
||||
// End of action, parse params
|
||||
if len(paramsData) > 0 {
|
||||
params := strings.Join(paramsData, "\n")
|
||||
err := action.Params.Parse(params)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// Remove ID from params if present
|
||||
delete(action.Params.GetAll(), "id")
|
||||
}
|
||||
comments = []string{}
|
||||
paramsData = []string{}
|
||||
action = nil
|
||||
} else {
|
||||
paramsData = append(paramsData, line)
|
||||
}
|
||||
}
|
||||
|
||||
// Handle comment state
|
||||
if state == StateCommentForActionMaybe {
|
||||
if strings.HasPrefix(lineStrip, "//") {
|
||||
comments = append(comments, strings.TrimLeft(lineStrip, "/ "))
|
||||
} else {
|
||||
if strings.HasPrefix(lineStrip, "!") {
|
||||
state = StateStart
|
||||
} else {
|
||||
state = StateStart
|
||||
p.OtherText += strings.Join(comments, "\n")
|
||||
if !strings.HasSuffix(p.OtherText, "\n") {
|
||||
p.OtherText += "\n"
|
||||
}
|
||||
comments = []string{}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Handle start state
|
||||
if state == StateStart {
|
||||
if strings.HasPrefix(lineStrip, "!") && !strings.HasPrefix(lineStrip, "![") {
|
||||
// Start a new action
|
||||
state = StateAction
|
||||
|
||||
// Create new action
|
||||
action = &Action{
|
||||
ID: p.NrActions + 1,
|
||||
Priority: priority,
|
||||
Params: paramsparser.New(),
|
||||
Result: paramsparser.New(),
|
||||
}
|
||||
p.NrActions++
|
||||
|
||||
// Set comments
|
||||
action.Comments = strings.Join(comments, "\n")
|
||||
comments = []string{}
|
||||
paramsData = []string{}
|
||||
|
||||
// Parse action name
|
||||
actionName := lineStrip
|
||||
if strings.Contains(lineStrip, " ") {
|
||||
actionName = strings.TrimSpace(strings.Split(lineStrip, " ")[0])
|
||||
params := strings.TrimSpace(strings.Join(strings.Split(lineStrip, " ")[1:], " "))
|
||||
if params != "" {
|
||||
paramsData = append(paramsData, params)
|
||||
}
|
||||
}
|
||||
|
||||
// Determine action type
|
||||
if strings.HasPrefix(actionName, "!!!!!") {
|
||||
return ErrInvalidActionPrefix
|
||||
} else if strings.HasPrefix(actionName, "!!!!") {
|
||||
action.ActionType = ActionTypeWAL
|
||||
} else if strings.HasPrefix(actionName, "!!!") {
|
||||
action.ActionType = ActionTypeMacro
|
||||
} else if strings.HasPrefix(actionName, "!!") {
|
||||
action.ActionType = ActionTypeSAL
|
||||
} else if strings.HasPrefix(actionName, "!") {
|
||||
action.ActionType = ActionTypeDAL
|
||||
}
|
||||
|
||||
// Remove prefix
|
||||
actionName = strings.TrimLeft(actionName, "!")
|
||||
|
||||
// Split into actor and action name
|
||||
parts := strings.Split(actionName, ".")
|
||||
if len(parts) == 1 {
|
||||
action.Actor = "core"
|
||||
action.Name = tools.NameFix(parts[0])
|
||||
} else if len(parts) == 2 {
|
||||
action.Actor = tools.NameFix(parts[0])
|
||||
action.Name = tools.NameFix(parts[1])
|
||||
} else {
|
||||
return ErrInvalidActionName
|
||||
}
|
||||
|
||||
// Add action to playbook
|
||||
p.Actions = append(p.Actions, action)
|
||||
|
||||
continue
|
||||
} else if strings.HasPrefix(lineStrip, "//") {
|
||||
state = StateCommentForActionMaybe
|
||||
comments = append(comments, strings.TrimLeft(lineStrip, "/ "))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Process the last action if needed
|
||||
if state == StateAction && action != nil && action.ID != 0 {
|
||||
if len(paramsData) > 0 {
|
||||
params := strings.Join(paramsData, "\n")
|
||||
err := action.Params.Parse(params)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// Remove ID from params if present
|
||||
delete(action.Params.GetAll(), "id")
|
||||
}
|
||||
}
|
||||
|
||||
// Process the last comment if needed
|
||||
if state == StateCommentForActionMaybe && len(comments) > 0 {
|
||||
p.OtherText += strings.Join(comments, "\n")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// NewFromFile creates a new PlayBook from a file
|
||||
func NewFromFile(path string, priority int) (*PlayBook, error) {
|
||||
// This is a simplified version - in a real implementation, you'd read the file
|
||||
// and handle different file types (md, hero, etc.)
|
||||
|
||||
// For now, we'll just create an empty playbook
|
||||
pb := New()
|
||||
|
||||
// TODO: Implement file reading and parsing
|
||||
|
||||
return pb, nil
|
||||
}
|
||||
|
||||
// Errors
|
||||
var (
|
||||
ErrInvalidActionPrefix = NewError("invalid action prefix")
|
||||
ErrInvalidActionName = NewError("invalid action name")
|
||||
)
|
||||
|
||||
// NewError creates a new error
|
||||
func NewError(msg string) error {
|
||||
return &PlayBookError{msg}
|
||||
}
|
||||
|
||||
// PlayBookError represents a playbook error
|
||||
type PlayBookError struct {
|
||||
Msg string
|
||||
}
|
||||
|
||||
// Error returns the error message
|
||||
func (e *PlayBookError) Error() string {
|
||||
return e.Msg
|
||||
}
|
301
_pkg2_dont_use/heroscript/playbook/playbook.go
Normal file
301
_pkg2_dont_use/heroscript/playbook/playbook.go
Normal file
@@ -0,0 +1,301 @@
|
||||
package playbook
|
||||
|
||||
import (
|
||||
"crypto/sha1"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"git.ourworld.tf/herocode/heroagent/pkg/heroscript/paramsparser"
|
||||
)
|
||||
|
||||
// ActionType represents the type of action
|
||||
type ActionType int
|
||||
|
||||
const (
|
||||
ActionTypeUnknown ActionType = iota
|
||||
ActionTypeDAL
|
||||
ActionTypeSAL
|
||||
ActionTypeWAL
|
||||
ActionTypeMacro
|
||||
)
|
||||
|
||||
// Action represents a single action in a heroscript
|
||||
type Action struct {
|
||||
ID int
|
||||
CID string
|
||||
Name string
|
||||
Actor string
|
||||
Priority int
|
||||
Params *paramsparser.ParamsParser
|
||||
Result *paramsparser.ParamsParser
|
||||
ActionType ActionType
|
||||
Comments string
|
||||
Done bool
|
||||
}
|
||||
|
||||
// PlayBook represents a collection of actions
|
||||
type PlayBook struct {
|
||||
Actions []*Action
|
||||
Priorities map[int][]int // key is priority, value is list of action indices
|
||||
OtherText string // text outside of actions
|
||||
Result string
|
||||
NrActions int
|
||||
Done []int
|
||||
}
|
||||
|
||||
// NewAction creates a new action and adds it to the playbook
|
||||
func (p *PlayBook) NewAction(cid, name, actor string, priority int, actionType ActionType) *Action {
|
||||
p.NrActions++
|
||||
action := &Action{
|
||||
ID: p.NrActions,
|
||||
CID: cid,
|
||||
Name: name,
|
||||
Actor: actor,
|
||||
Priority: priority,
|
||||
ActionType: actionType,
|
||||
Params: paramsparser.New(),
|
||||
Result: paramsparser.New(),
|
||||
}
|
||||
p.Actions = append(p.Actions, action)
|
||||
return action
|
||||
}
|
||||
|
||||
// New creates a new PlayBook
|
||||
func New() *PlayBook {
|
||||
return &PlayBook{
|
||||
Actions: make([]*Action, 0),
|
||||
Priorities: make(map[int][]int),
|
||||
NrActions: 0,
|
||||
Done: make([]int, 0),
|
||||
}
|
||||
}
|
||||
|
||||
// NewFromText creates a new PlayBook from heroscript text
|
||||
func NewFromText(text string) (*PlayBook, error) {
|
||||
pb := New()
|
||||
err := pb.AddText(text, 10) // Default priority 10
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return pb, nil
|
||||
}
|
||||
|
||||
// String returns the heroscript representation of the action
|
||||
func (a *Action) String() string {
|
||||
out := a.HeroScript()
|
||||
if a.Result != nil && len(a.Result.GetAll()) > 0 {
|
||||
out += "\n\nResult:\n"
|
||||
// Indent the result
|
||||
resultParams := a.Result.GetAll()
|
||||
for k, v := range resultParams {
|
||||
out += " " + k + ": '" + v + "'\n"
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// HeroScript returns the heroscript representation of the action
|
||||
func (a *Action) HeroScript() string {
|
||||
var out strings.Builder
|
||||
|
||||
// Add comments if any
|
||||
if a.Comments != "" {
|
||||
lines := strings.Split(a.Comments, "\n")
|
||||
for _, line := range lines {
|
||||
out.WriteString("// " + line + "\n")
|
||||
}
|
||||
}
|
||||
|
||||
// Add action type prefix
|
||||
switch a.ActionType {
|
||||
case ActionTypeDAL:
|
||||
out.WriteString("!")
|
||||
case ActionTypeSAL:
|
||||
out.WriteString("!!")
|
||||
case ActionTypeMacro:
|
||||
out.WriteString("!!!")
|
||||
default:
|
||||
out.WriteString("!!") // Default to SAL
|
||||
}
|
||||
|
||||
// Add actor and name
|
||||
if a.Actor != "" {
|
||||
out.WriteString(a.Actor + ".")
|
||||
}
|
||||
out.WriteString(a.Name + " ")
|
||||
|
||||
// Add ID if present
|
||||
if a.ID > 0 {
|
||||
out.WriteString(fmt.Sprintf("id:%d ", a.ID))
|
||||
}
|
||||
|
||||
// Add parameters
|
||||
if a.Params != nil && len(a.Params.GetAll()) > 0 {
|
||||
params := a.Params.GetAll()
|
||||
firstLine := true
|
||||
for k, v := range params {
|
||||
if firstLine {
|
||||
out.WriteString(k + ":'" + v + "'\n")
|
||||
firstLine = false
|
||||
} else {
|
||||
out.WriteString(" " + k + ":'" + v + "'\n")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return out.String()
|
||||
}
|
||||
|
||||
// HashKey returns a unique hash for the action
|
||||
func (a *Action) HashKey() string {
|
||||
h := sha1.New()
|
||||
h.Write([]byte(a.HeroScript()))
|
||||
return hex.EncodeToString(h.Sum(nil))
|
||||
}
|
||||
|
||||
// HashKey returns a unique hash for the playbook
|
||||
func (p *PlayBook) HashKey() string {
|
||||
h := sha1.New()
|
||||
for _, action := range p.Actions {
|
||||
h.Write([]byte(action.HashKey()))
|
||||
}
|
||||
return hex.EncodeToString(h.Sum(nil))
|
||||
}
|
||||
|
||||
// HeroScript returns the heroscript representation of the playbook
|
||||
func (p *PlayBook) HeroScript(showDone bool) string {
|
||||
var out strings.Builder
|
||||
|
||||
actions, _ := p.ActionsSorted(false)
|
||||
for _, action := range actions {
|
||||
if !showDone && action.Done {
|
||||
continue
|
||||
}
|
||||
out.WriteString(action.HeroScript() + "\n")
|
||||
}
|
||||
|
||||
if p.OtherText != "" {
|
||||
out.WriteString(p.OtherText)
|
||||
}
|
||||
|
||||
return out.String()
|
||||
}
|
||||
|
||||
// ActionsSorted returns the actions sorted by priority
|
||||
func (p *PlayBook) ActionsSorted(prioOnly bool) ([]*Action, error) {
|
||||
var result []*Action
|
||||
|
||||
// If no priorities are set, return all actions
|
||||
if len(p.Priorities) == 0 {
|
||||
return p.Actions, nil
|
||||
}
|
||||
|
||||
// Get all priority numbers and sort them
|
||||
var priorities []int
|
||||
for prio := range p.Priorities {
|
||||
priorities = append(priorities, prio)
|
||||
}
|
||||
sort.Ints(priorities)
|
||||
|
||||
// Add actions in priority order
|
||||
for _, prio := range priorities {
|
||||
if prioOnly && prio > 49 {
|
||||
continue
|
||||
}
|
||||
|
||||
actionIDs := p.Priorities[prio]
|
||||
for _, id := range actionIDs {
|
||||
action, err := p.GetAction(id, "", "")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
result = append(result, action)
|
||||
}
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// GetAction finds an action by ID, actor, or name
|
||||
func (p *PlayBook) GetAction(id int, actor, name string) (*Action, error) {
|
||||
actions, err := p.FindActions(id, actor, name, ActionTypeUnknown)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(actions) == 1 {
|
||||
return actions[0], nil
|
||||
} else if len(actions) == 0 {
|
||||
return nil, fmt.Errorf("couldn't find action with id: %d, actor: %s, name: %s", id, actor, name)
|
||||
} else {
|
||||
return nil, fmt.Errorf("multiple actions found with id: %d, actor: %s, name: %s", id, actor, name)
|
||||
}
|
||||
}
|
||||
|
||||
// FindActions finds actions based on criteria
|
||||
func (p *PlayBook) FindActions(id int, actor, name string, actionType ActionType) ([]*Action, error) {
|
||||
var result []*Action
|
||||
|
||||
for _, a := range p.Actions {
|
||||
// If ID is specified, return only the action with that ID
|
||||
if id != 0 {
|
||||
if a.ID == id {
|
||||
return []*Action{a}, nil
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
// Filter by actor if specified
|
||||
if actor != "" && a.Actor != actor {
|
||||
continue
|
||||
}
|
||||
|
||||
// Filter by name if specified
|
||||
if name != "" && a.Name != name {
|
||||
continue
|
||||
}
|
||||
|
||||
// Filter by actionType if specified
|
||||
if actionType != ActionTypeUnknown && a.ActionType != actionType {
|
||||
continue
|
||||
}
|
||||
|
||||
// If the action passes all filters, add it to the result
|
||||
result = append(result, a)
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// ActionExists checks if an action exists
|
||||
func (p *PlayBook) ActionExists(id int, actor, name string) bool {
|
||||
actions, err := p.FindActions(id, actor, name, ActionTypeUnknown)
|
||||
if err != nil || len(actions) == 0 {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// String returns a string representation of the playbook
|
||||
func (p *PlayBook) String() string {
|
||||
return p.HeroScript(true)
|
||||
}
|
||||
|
||||
// EmptyCheck checks if there are any actions left to execute
|
||||
func (p *PlayBook) EmptyCheck() error {
|
||||
var undoneActions []*Action
|
||||
|
||||
for _, a := range p.Actions {
|
||||
if !a.Done {
|
||||
undoneActions = append(undoneActions, a)
|
||||
}
|
||||
}
|
||||
|
||||
if len(undoneActions) > 0 {
|
||||
return fmt.Errorf("there are actions left to execute: %d", len(undoneActions))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
211
_pkg2_dont_use/heroscript/playbook/playbook_test.go
Normal file
211
_pkg2_dont_use/heroscript/playbook/playbook_test.go
Normal file
@@ -0,0 +1,211 @@
|
||||
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)
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user