diff --git a/internal/github/actions.go b/internal/github/actions.go index b464e63..a22e7da 100644 --- a/internal/github/actions.go +++ b/internal/github/actions.go @@ -34,6 +34,9 @@ type ActionRef struct { // - "actions/checkout@v5" -> {Owner: "actions", Repo: "checkout", Version: "v5"} // - "actions/setup-node@v4" -> {Owner: "actions", Repo: "setup-node", Version: "v4"} func ParseActionRef(ref string) (*ActionRef, error) { + // Trim whitespace (including newlines, spaces, tabs) + ref = strings.TrimSpace(ref) + if ref == "" { return nil, fmt.Errorf("action reference cannot be empty") } diff --git a/internal/github/actions_test.go b/internal/github/actions_test.go new file mode 100644 index 0000000..b40cc64 --- /dev/null +++ b/internal/github/actions_test.go @@ -0,0 +1,110 @@ +package github + +import ( + "testing" +) + +func TestParseActionRef(t *testing.T) { + tests := []struct { + name string + input string + wantOwner string + wantRepo string + wantVersion string + wantErr bool + }{ + { + name: "valid action reference", + input: "actions/checkout@v5", + wantOwner: "actions", + wantRepo: "checkout", + wantVersion: "v5", + wantErr: false, + }, + { + name: "valid action with trailing newline", + input: "actions/checkout@v5\n", + wantOwner: "actions", + wantRepo: "checkout", + wantVersion: "v5", + wantErr: false, + }, + { + name: "valid action with leading and trailing whitespace", + input: " actions/checkout@v5 ", + wantOwner: "actions", + wantRepo: "checkout", + wantVersion: "v5", + wantErr: false, + }, + { + name: "valid action with tabs and newlines", + input: "\t\nactions/checkout@v5\n\t", + wantOwner: "actions", + wantRepo: "checkout", + wantVersion: "v5", + wantErr: false, + }, + { + name: "complex action reference with whitespace", + input: "kula-app/wait-for-services-action@v1\n", + wantOwner: "kula-app", + wantRepo: "wait-for-services-action", + wantVersion: "v1", + wantErr: false, + }, + { + name: "empty string", + input: "", + wantErr: true, + }, + { + name: "only whitespace", + input: " \n\t ", + wantErr: true, + }, + { + name: "missing version", + input: "actions/checkout", + wantErr: true, + }, + { + name: "missing repo", + input: "actions@v5", + wantErr: true, + }, + { + name: "invalid format - too many slashes", + input: "actions/github/checkout@v5", + wantErr: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := ParseActionRef(tt.input) + + if tt.wantErr { + if err == nil { + t.Errorf("ParseActionRef() expected error but got none") + } + return + } + + if err != nil { + t.Errorf("ParseActionRef() unexpected error: %v", err) + return + } + + if got.Owner != tt.wantOwner { + t.Errorf("ParseActionRef() Owner = %v, want %v", got.Owner, tt.wantOwner) + } + if got.Repo != tt.wantRepo { + t.Errorf("ParseActionRef() Repo = %v, want %v", got.Repo, tt.wantRepo) + } + if got.Version != tt.wantVersion { + t.Errorf("ParseActionRef() Version = %v, want %v", got.Version, tt.wantVersion) + } + }) + } +}