diff --git a/cmd/slackdump/internal/workspace/workspace.go b/cmd/slackdump/internal/workspace/workspace.go index 1d786a18..03b3039b 100644 --- a/cmd/slackdump/internal/workspace/workspace.go +++ b/cmd/slackdump/internal/workspace/workspace.go @@ -4,6 +4,7 @@ import ( "context" "errors" "fmt" + "log/slog" "os" "runtime/trace" "strings" @@ -23,7 +24,7 @@ var CmdWorkspace = &base.Command{ Run: nil, Wizard: nil, UsageLine: baseCommand, - Short: "add or choose already existing workspace to run on", + Short: "manage Slack Workspaces", Long: ` # Workspace Command @@ -31,9 +32,10 @@ Slackdump supports working with multiple Slack Workspaces without the need to authenticate again (unless login credentials are expired or became invalid due to some other reason). -**Workspace** command allows to **add** a new Slack Workspace, **list** already +**Workspace** command allows to add a **new** Slack Workspace, **list** already authenticated workspaces, **select** a workspace that you have previously -logged in to, or **del**ete an existing workspace. +logged in to, **del**ete an existing workspace, or **import** credentials from +an environment file. To learn more about different login options, run: @@ -92,6 +94,7 @@ func AuthCurrent(ctx context.Context, cacheDir string, overrideWsp string, usePl return nil, err } trace.Logf(ctx, "AuthCurrent", "current workspace=%s", wsp) + slog.DebugContext(ctx, "current", "workspace", wsp) prov, err := authWsp(ctx, cacheDir, wsp, usePlaywright) if err != nil { @@ -163,17 +166,23 @@ func CacheMgr(opts ...cache.Option) (*cache.Manager, error) { return cache.NewManager(cfg.CacheDir(), opts...) } +// exported for testing +var ( + authCurrent = AuthCurrent + showUI = workspaceui.ShowUI +) + func CurrentOrNewProviderCtx(ctx context.Context) (context.Context, error) { cachedir := cfg.CacheDir() - prov, err := AuthCurrent(ctx, cachedir, cfg.Workspace, cfg.LegacyBrowser) + prov, err := authCurrent(ctx, cachedir, cfg.Workspace, cfg.LegacyBrowser) if err != nil { if errors.Is(err, cache.ErrNoWorkspaces) { // ask to create a new workspace - if err := workspaceui.ShowUI(ctx, workspaceui.WithQuickLogin(), workspaceui.WithTitle("No workspaces, please choose a login method")); err != nil { + if err := showUI(ctx, workspaceui.WithQuickLogin(), workspaceui.WithTitle("No workspaces, please choose a login method")); err != nil { return ctx, fmt.Errorf("auth error: %w", err) } // one more time... - prov, err = AuthCurrent(ctx, cachedir, cfg.Workspace, cfg.LegacyBrowser) + prov, err = authCurrent(ctx, cachedir, cfg.Workspace, cfg.LegacyBrowser) if err != nil { return ctx, err } diff --git a/cmd/slackdump/internal/workspace/workspace_test.go b/cmd/slackdump/internal/workspace/workspace_test.go index ed1918cf..1918fa9f 100644 --- a/cmd/slackdump/internal/workspace/workspace_test.go +++ b/cmd/slackdump/internal/workspace/workspace_test.go @@ -1,10 +1,17 @@ package workspace import ( + "context" "os" "path/filepath" + "reflect" "testing" + "github.com/stretchr/testify/assert" + + "github.com/rusq/slackdump/v3/auth" + "github.com/rusq/slackdump/v3/cmd/slackdump/internal/workspace/workspaceui" + "github.com/rusq/slackdump/v3/internal/cache" fx "github.com/rusq/slackdump/v3/internal/fixtures" ) @@ -54,7 +61,7 @@ func Test_argsWorkspace(t *testing.T) { } func TestCurrent(t *testing.T) { - //fixtures + // fixtures empty := t.TempDir() @@ -65,17 +72,17 @@ func TestCurrent(t *testing.T) { // case2 has files, and a pointer to the current workspace. case2 := t.TempDir() fx.PrepareDir(t, case2, "dummy", fx.WorkspaceFiles...) - os.WriteFile(filepath.Join(case2, "workspace.txt"), []byte(fx.StripExt(fx.WorkspaceFiles[0])+"\n"), 0644) + os.WriteFile(filepath.Join(case2, "workspace.txt"), []byte(fx.StripExt(fx.WorkspaceFiles[0])+"\n"), 0o644) // case3 has a file, which is specified as a directory to the function // so that manager fails to initialise. case3 := t.TempDir() - os.WriteFile(filepath.Join(case3, "cache_dir"), []byte(""), 0644) + os.WriteFile(filepath.Join(case3, "cache_dir"), []byte(""), 0o644) // case4 workspace pointer points to non-existing file. case4 := t.TempDir() fx.PrepareDir(t, case4, "dummy", fx.WorkspaceFiles...) - os.WriteFile(filepath.Join(case4, "workspace.txt"), []byte("doesnotexist\n"), 0644) + os.WriteFile(filepath.Join(case4, "workspace.txt"), []byte("doesnotexist\n"), 0o644) // tests type args struct { @@ -150,3 +157,89 @@ func TestCurrent(t *testing.T) { }) } } + +type recorder struct { + authCurrentCalledTimes int + authCurrentRetProv auth.Provider + authCurrentRetErr error + + showUICalledTimes int + showUIRetErr error +} + +func (r *recorder) AuthCurrent(ctx context.Context, cacheDir string, overrideWsp string, usePlaywright bool) (auth.Provider, error) { + r.authCurrentCalledTimes++ + return r.authCurrentRetProv, r.authCurrentRetErr +} + +func (r *recorder) ShowUI(ctx context.Context, opts ...workspaceui.UIOption) error { + r.showUICalledTimes++ + return r.showUIRetErr +} + +func TestCurrentOrNewProviderCtx(t *testing.T) { + type args struct { + ctx context.Context + } + tests := []struct { + name string + args args + rec *recorder + want context.Context + wantErr bool + wantAuthCurrentCalledTimes int + wantShowUICalledTimes int + }{ + { + "authCurrent fails", + args{context.Background()}, + &recorder{ + authCurrentRetErr: assert.AnError, + }, + context.Background(), + true, + 1, + 0, + }, + { + "authCurrent doesn't find workspace", + args{context.Background()}, + &recorder{ + authCurrentRetErr: cache.ErrNoWorkspaces, + }, + context.Background(), + true, + 2, // attempts to call authCurrent twice + 1, // after showing the UI + }, + { + "authCurrent succeeds", + args{context.Background()}, + &recorder{ + authCurrentRetProv: auth.ValueAuth{}, + }, + auth.WithContext(context.Background(), auth.ValueAuth{}), + false, + 1, + 0, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + rec := tt.rec + authCurrent = rec.AuthCurrent + showUI = rec.ShowUI + + got, err := CurrentOrNewProviderCtx(tt.args.ctx) + if (err != nil) != tt.wantErr { + t.Errorf("CurrentOrNewProviderCtx() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("CurrentOrNewProviderCtx() = %v, want %v", got, tt.want) + } + assert.Equal(t, rec.authCurrentCalledTimes, tt.wantAuthCurrentCalledTimes) + assert.Equal(t, rec.showUICalledTimes, tt.wantShowUICalledTimes) + }) + } +}