diff --git a/cmd/doctor/check.go b/cmd/doctor/check.go index e3c54df9..ec0b190d 100644 --- a/cmd/doctor/check.go +++ b/cmd/doctor/check.go @@ -19,6 +19,7 @@ import ( "fmt" "math/rand" "runtime" + "slices" "strings" "time" @@ -27,6 +28,7 @@ import ( "github.com/slackapi/slack-cli/internal/iostreams" "github.com/slackapi/slack-cli/internal/pkg/version" "github.com/slackapi/slack-cli/internal/shared" + "github.com/slackapi/slack-cli/internal/shared/types" "github.com/slackapi/slack-cli/internal/slackerror" "github.com/slackapi/slack-cli/internal/style" "github.com/slackapi/slack-cli/internal/update" @@ -271,6 +273,15 @@ func checkCLICreds(ctx context.Context, clients *shared.ClientFactory) (Section, section.Errors = []slackerror.Error{*slackerror.New(slackerror.ErrNotAuthed)} } + slices.SortStableFunc(authList, func(a types.SlackAuth, b types.SlackAuth) int { + domain := strings.Compare(a.TeamDomain, b.TeamDomain) + if domain != 0 { + return domain + } + id := strings.Compare(a.TeamID, b.TeamID) + return id + }) + // Teams if len(authList) > 0 { authSections := []Section{} @@ -278,25 +289,41 @@ func checkCLICreds(ctx context.Context, clients *shared.ClientFactory) (Section, caser := cases.Title(language.English) for _, authInfo := range authList { checkDetails := []Section{ - {"Team domain", authInfo.TeamDomain, []Section{}, []slackerror.Error{}}, - {"Team ID", authInfo.TeamID, []Section{}, []slackerror.Error{}}, - {"User ID", authInfo.UserID, []Section{}, []slackerror.Error{}}, { - "Last updated", - authInfo.LastUpdated.Format("2006-01-02 15:04:05 Z07:00"), - []Section{}, - []slackerror.Error{}, + Label: "Team domain", + Value: authInfo.TeamDomain, + }, + { + Label: "Team ID", + Value: authInfo.TeamID, + }, + { + Label: "User ID", + Value: authInfo.UserID, + }, + { + Label: "Last updated", + Value: authInfo.LastUpdated.Format("2006-01-02 15:04:05 Z07:00"), + }, + { + Label: "Authorization level", + Value: caser.String(authInfo.AuthLevel()), }, - {"Authorization level", caser.String(authInfo.AuthLevel()), []Section{}, []slackerror.Error{}}, } if authInfo.APIHost != nil { - hostSection := Section{"API Host", *authInfo.APIHost, []Section{}, []slackerror.Error{}} + hostSection := Section{ + Label: "API Host", + Value: *authInfo.APIHost, + } checkDetails = append(checkDetails, hostSection) } // Validate session token - validitySection := Section{"Token status", "Valid", []Section{}, []slackerror.Error{}} + validitySection := Section{ + Label: "Token status", + Value: "Valid", + } // TODO :: .ValidateSession() utilizes the host (APIHost) assigned to the client making // the call. This results in incorrectly deeming tokens invalid if using multiple workspaces diff --git a/cmd/doctor/check_test.go b/cmd/doctor/check_test.go index f553df25..d608c1e2 100644 --- a/cmd/doctor/check_test.go +++ b/cmd/doctor/check_test.go @@ -19,6 +19,7 @@ import ( "runtime" "testing" + "github.com/slackapi/slack-cli/internal/api" "github.com/slackapi/slack-cli/internal/config" "github.com/slackapi/slack-cli/internal/deputil" "github.com/slackapi/slack-cli/internal/hooks" @@ -294,23 +295,148 @@ func TestDoctorCheckCLIConfig(t *testing.T) { } func TestDoctorCheckCLICreds(t *testing.T) { + mockAPIHost := "https://example.com/api/" tests := map[string]struct { - auths types.SlackAuth + mockAuths []types.SlackAuth + expectedSections []Section + expectedErrorSection []slackerror.Error }{ - "errors without available authorizations": {}, + "errors without available authorizations": { + expectedErrorSection: []slackerror.Error{*slackerror.New(slackerror.ErrNotAuthed)}, + expectedSections: []Section{}, + }, + "orders multiple different authentications": { + mockAuths: []types.SlackAuth{ + { + TeamDomain: "teamB", + TeamID: "T002", + UserID: "U002", + }, + { + TeamDomain: "teamB", + TeamID: "E003", + EnterpriseID: "E003", + IsEnterpriseInstall: true, + UserID: "U003", + }, + { + APIHost: &mockAPIHost, + TeamDomain: "teamA", + TeamID: "T004", + UserID: "U004", + }, + }, + expectedSections: []Section{ + { + Subsections: []Section{ + { + Label: "Team domain", + Value: "teamA", + }, + { + Label: "Team ID", + Value: "T004", + }, + { + Label: "User ID", + Value: "U004", + }, + { + Label: "Last updated", + Value: "0001-01-01 00:00:00 Z", + }, + { + Label: "Authorization level", + Value: "Workspace", + }, + { + Label: "API Host", + Value: "https://example.com/api/", + }, + { + Label: "Token status", + Value: "Valid", + }, + }, + Errors: []slackerror.Error{}, + }, + { + Subsections: []Section{ + { + Label: "Team domain", + Value: "teamB", + }, + { + Label: "Team ID", + Value: "E003", + }, + { + Label: "User ID", + Value: "U003", + }, + { + Label: "Last updated", + Value: "0001-01-01 00:00:00 Z", + }, + { + Label: "Authorization level", + Value: "Organization", + }, + { + Label: "Token status", + Value: "Valid", + }, + }, + Errors: []slackerror.Error{}, + }, + { + Subsections: []Section{ + { + Label: "Team domain", + Value: "teamB", + }, + { + Label: "Team ID", + Value: "T002", + }, + { + Label: "User ID", + Value: "U002", + }, + { + Label: "Last updated", + Value: "0001-01-01 00:00:00 Z", + }, + { + Label: "Authorization level", + Value: "Workspace", + }, + { + Label: "Token status", + Value: "Valid", + }, + }, + Errors: []slackerror.Error{}, + }, + }, + expectedErrorSection: []slackerror.Error{}, + }, } - for name := range tests { + for name, tt := range tests { t.Run(name, func(t *testing.T) { ctx := slackcontext.MockContext(t.Context()) clientsMock := shared.NewClientsMock() + clientsMock.Auth.On("Auths", mock.Anything).Return(tt.mockAuths, nil) + clientsMock.Auth.On("ResolveAPIHost", mock.Anything, mock.Anything, mock.Anything).Return("https://slack.com/api/", nil) + clientsMock.API.On("ValidateSession", mock.Anything, mock.Anything).Return(api.AuthSession{}, nil) clientsMock.AddDefaultMocks() clients := shared.NewClientFactory(clientsMock.MockClientFactory()) expected := Section{ Label: "Credentials", Value: "your Slack authentication", - Subsections: []Section{}, - Errors: []slackerror.Error{*slackerror.New(slackerror.ErrNotAuthed)}, + Subsections: tt.expectedSections, + Errors: tt.expectedErrorSection, } section, err := checkCLICreds(ctx, clients) diff --git a/cmd/doctor/doctor_test.go b/cmd/doctor/doctor_test.go index 44d9f8ec..61d47c9f 100644 --- a/cmd/doctor/doctor_test.go +++ b/cmd/doctor/doctor_test.go @@ -178,40 +178,28 @@ func TestDoctorCommand(t *testing.T) { Value: "", Subsections: []Section{ { - Label: "Team domain", - Value: expectedCredentials.TeamDomain, - Subsections: []Section{}, - Errors: []slackerror.Error{}, + Label: "Team domain", + Value: expectedCredentials.TeamDomain, }, { - Label: "Team ID", - Value: expectedCredentials.TeamID, - Subsections: []Section{}, - Errors: []slackerror.Error{}, + Label: "Team ID", + Value: expectedCredentials.TeamID, }, { - Label: "User ID", - Value: expectedCredentials.UserID, - Subsections: []Section{}, - Errors: []slackerror.Error{}, + Label: "User ID", + Value: expectedCredentials.UserID, }, { - Label: "Last updated", - Value: expectedUpdateTime, - Subsections: []Section{}, - Errors: []slackerror.Error{}, + Label: "Last updated", + Value: expectedUpdateTime, }, { - Label: "Authorization level", - Value: "Workspace", - Subsections: []Section{}, - Errors: []slackerror.Error{}, + Label: "Authorization level", + Value: "Workspace", }, { - Label: "Token status", - Value: "Valid", - Subsections: []Section{}, - Errors: []slackerror.Error{}, + Label: "Token status", + Value: "Valid", }, }, Errors: []slackerror.Error{},