...
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