package setup_test import ( "bytes" "context" "errors" "fmt" "strconv" "strings" "testing" "github.com/golang/mock/gomock" "github.com/influxdata/influx-cli/v2/api" "github.com/influxdata/influx-cli/v2/clients" "github.com/influxdata/influx-cli/v2/clients/setup" "github.com/influxdata/influx-cli/v2/config" "github.com/influxdata/influx-cli/v2/internal/mock" "github.com/influxdata/influx-cli/v2/internal/testutils" "github.com/influxdata/influx-cli/v2/pkg/duration" "github.com/stretchr/testify/assert" tmock "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" ) func Test_SetupConfigNameCollision(t *testing.T) { t.Parallel() ctrl := gomock.NewController(t) client := mock.NewMockSetupApi(ctrl) client.EXPECT().GetSetup(gomock.Any()).Return(api.ApiGetSetupRequest{ApiService: client}) client.EXPECT().GetSetupExecute(gomock.Any()).Return(api.InlineResponse200{Allowed: api.PtrBool(true)}, nil) cfg := "foo" configSvc := mock.NewMockConfigService(ctrl) configSvc.EXPECT().ListConfigs().Return(map[string]config.Config{cfg: {}}, nil) cli := setup.Client{ CLI: clients.CLI{ConfigService: configSvc}, SetupApi: client, } err := cli.Setup(context.Background(), &setup.Params{ConfigName: cfg}) require.Error(t, err) require.Contains(t, err.Error(), cfg) require.Contains(t, err.Error(), "already exists") } func Test_SetupConfigNameRequired(t *testing.T) { t.Parallel() ctrl := gomock.NewController(t) client := mock.NewMockSetupApi(ctrl) client.EXPECT().GetSetup(gomock.Any()).Return(api.ApiGetSetupRequest{ApiService: client}) client.EXPECT().GetSetupExecute(gomock.Any()).Return(api.InlineResponse200{Allowed: api.PtrBool(true)}, nil) configSvc := mock.NewMockConfigService(ctrl) configSvc.EXPECT().ListConfigs().Return(map[string]config.Config{"foo": {}}, nil) cli := setup.Client{ CLI: clients.CLI{ConfigService: configSvc}, SetupApi: client, } err := cli.Setup(context.Background(), &setup.Params{}) require.Error(t, err) require.Equal(t, setup.ErrConfigNameRequired, err) } func Test_SetupAlreadySetup(t *testing.T) { t.Parallel() ctrl := gomock.NewController(t) client := mock.NewMockSetupApi(ctrl) client.EXPECT().GetSetup(gomock.Any()).Return(api.ApiGetSetupRequest{ApiService: client}) client.EXPECT().GetSetupExecute(gomock.Any()).Return(api.InlineResponse200{Allowed: api.PtrBool(false)}, nil) configSvc := mock.NewMockConfigService(ctrl) cli := setup.Client{ CLI: clients.CLI{ConfigService: configSvc}, SetupApi: client, } err := cli.Setup(context.Background(), &setup.Params{}) require.Error(t, err) require.Equal(t, setup.ErrAlreadySetUp, err) } func Test_SetupCheckFailed(t *testing.T) { t.Parallel() e := "oh no" ctrl := gomock.NewController(t) client := mock.NewMockSetupApi(ctrl) client.EXPECT().GetSetup(gomock.Any()).Return(api.ApiGetSetupRequest{ApiService: client}) client.EXPECT().GetSetupExecute(gomock.Any()).Return(api.InlineResponse200{}, errors.New(e)) configSvc := mock.NewMockConfigService(ctrl) cli := setup.Client{ CLI: clients.CLI{ConfigService: configSvc}, SetupApi: client, } err := cli.Setup(context.Background(), &setup.Params{}) require.Error(t, err) require.Contains(t, err.Error(), e) } func Test_SetupSuccessNoninteractive(t *testing.T) { t.Parallel() retentionSecs := int64(duration.Week.Seconds()) params := setup.Params{ Username: "user", Password: "mysecretpassword", AuthToken: "mytoken", Org: "org", Bucket: "bucket", Retention: fmt.Sprintf("%ds", retentionSecs), Force: true, ConfigName: "my-config", } resp := api.OnboardingResponse{ Auth: &api.Authorization{Token: ¶ms.AuthToken}, Org: &api.Organization{Name: params.Org}, User: &api.UserResponse{Name: params.Username}, Bucket: &api.Bucket{Name: params.Bucket}, } ctrl := gomock.NewController(t) client := mock.NewMockSetupApi(ctrl) client.EXPECT().GetSetup(gomock.Any()).Return(api.ApiGetSetupRequest{ApiService: client}) client.EXPECT().GetSetupExecute(gomock.Any()).Return(api.InlineResponse200{Allowed: api.PtrBool(true)}, nil) client.EXPECT().PostSetup(gomock.Any()).Return(api.ApiPostSetupRequest{ApiService: client}) client.EXPECT().PostSetupExecute(tmock.MatchedBy(func(in api.ApiPostSetupRequest) bool { body := in.GetOnboardingRequest() return assert.NotNil(t, body) && assert.Equal(t, params.Username, body.Username) && assert.Equal(t, params.Password, *body.Password) && assert.Equal(t, params.AuthToken, *body.Token) && assert.Equal(t, params.Org, body.Org) && assert.Equal(t, params.Bucket, body.Bucket) && assert.Equal(t, retentionSecs, *body.RetentionPeriodSeconds) })).Return(resp, nil) configSvc := mock.NewMockConfigService(ctrl) configSvc.EXPECT().ListConfigs().Return(nil, nil) configSvc.EXPECT().CreateConfig(tmock.MatchedBy(func(in config.Config) bool { return assert.Equal(t, params.ConfigName, in.Name) && assert.Equal(t, params.AuthToken, in.Token) && assert.Equal(t, config.DefaultConfig.Host, in.Host) && assert.Equal(t, params.Org, in.Org) && assert.True(t, in.Active) })).DoAndReturn(func(in config.Config) (config.Config, error) { return in, nil }) stdio := mock.NewMockStdIO(ctrl) bytesWritten := bytes.Buffer{} stdio.EXPECT().Write(gomock.Any()).DoAndReturn(bytesWritten.Write).AnyTimes() cli := setup.Client{ CLI: clients.CLI{ConfigService: configSvc, ActiveConfig: config.Config{}, StdIO: stdio}, SetupApi: client, } require.NoError(t, cli.Setup(context.Background(), ¶ms)) testutils.MatchLines(t, []string{ `User\s+Organization\s+Bucket`, fmt.Sprintf(`%s\s+%s\s+%s`, params.Username, params.Org, params.Bucket), }, strings.Split(bytesWritten.String(), "\n")) } func Test_SetupSuccessInteractive(t *testing.T) { t.Parallel() retentionSecs := int64(duration.Week.Seconds()) retentionHrs := int(duration.Week.Hours()) username := "user" password := "mysecretpassword" token := "mytoken" org := "org" bucket := "bucket" resp := api.OnboardingResponse{ Auth: &api.Authorization{Token: &token}, Org: &api.Organization{Name: org}, User: &api.UserResponse{Name: username}, Bucket: &api.Bucket{Name: bucket}, } ctrl := gomock.NewController(t) client := mock.NewMockSetupApi(ctrl) client.EXPECT().GetSetup(gomock.Any()).Return(api.ApiGetSetupRequest{ApiService: client}) client.EXPECT().GetSetupExecute(gomock.Any()).Return(api.InlineResponse200{Allowed: api.PtrBool(true)}, nil) client.EXPECT().PostSetup(gomock.Any()).Return(api.ApiPostSetupRequest{ApiService: client}) client.EXPECT().PostSetupExecute(tmock.MatchedBy(func(in api.ApiPostSetupRequest) bool { body := in.GetOnboardingRequest() return assert.NotNil(t, body) && assert.Equal(t, username, body.Username) && assert.Equal(t, password, *body.Password) && assert.Nil(t, body.Token) && assert.Equal(t, org, body.Org) && assert.Equal(t, bucket, body.Bucket) && assert.Equal(t, retentionSecs, *body.RetentionPeriodSeconds) })).Return(resp, nil) configSvc := mock.NewMockConfigService(ctrl) configSvc.EXPECT().ListConfigs().Return(nil, nil) configSvc.EXPECT().CreateConfig(tmock.MatchedBy(func(in config.Config) bool { return assert.Equal(t, config.DefaultConfig.Name, in.Name) && assert.Equal(t, token, in.Token) && assert.Equal(t, config.DefaultConfig.Host, in.Host) && assert.Equal(t, org, in.Org) && assert.True(t, in.Active) })).DoAndReturn(func(in config.Config) (config.Config, error) { return in, nil }) stdio := mock.NewMockStdIO(ctrl) bytesWritten := bytes.Buffer{} stdio.EXPECT().Write(gomock.Any()).DoAndReturn(bytesWritten.Write).AnyTimes() stdio.EXPECT().Banner(gomock.Any()) stdio.EXPECT().GetStringInput(gomock.Eq("Please type your primary username"), gomock.Any()).Return(username, nil) stdio.EXPECT().GetPassword(gomock.Eq("Please type your password")).Return(password, nil) stdio.EXPECT().GetStringInput(gomock.Eq("Please type your primary organization name"), gomock.Any()).Return(org, nil) stdio.EXPECT().GetStringInput("Please type your primary bucket name", gomock.Any()).Return(bucket, nil) stdio.EXPECT().GetStringInput("Please type your retention period in hours, or 0 for infinite", gomock.Any()).Return(strconv.Itoa(retentionHrs), nil) stdio.EXPECT().GetConfirm(gomock.Any()).Return(true) cli := setup.Client{ CLI: clients.CLI{ConfigService: configSvc, ActiveConfig: config.Config{}, StdIO: stdio}, SetupApi: client, } require.NoError(t, cli.Setup(context.Background(), &setup.Params{})) testutils.MatchLines(t, []string{ `User\s+Organization\s+Bucket`, fmt.Sprintf(`%s\s+%s\s+%s`, username, org, bucket), }, strings.Split(bytesWritten.String(), "\n")) } func Test_SetupPasswordParamTooShort(t *testing.T) { t.Parallel() retentionSecs := int64(duration.Week.Seconds()) params := setup.Params{ Username: "user", Password: "2short", AuthToken: "mytoken", Org: "org", Bucket: "bucket", Retention: fmt.Sprintf("%ds", retentionSecs), Force: false, } ctrl := gomock.NewController(t) client := mock.NewMockSetupApi(ctrl) client.EXPECT().GetSetup(gomock.Any()).Return(api.ApiGetSetupRequest{ApiService: client}) client.EXPECT().GetSetupExecute(gomock.Any()).Return(api.InlineResponse200{Allowed: api.PtrBool(true)}, nil) configSvc := mock.NewMockConfigService(ctrl) configSvc.EXPECT().ListConfigs().Return(nil, nil) stdio := mock.NewMockStdIO(ctrl) cli := setup.Client{ CLI: clients.CLI{ConfigService: configSvc, ActiveConfig: config.Config{}, StdIO: stdio}, SetupApi: client, } err := cli.Setup(context.Background(), ¶ms) require.Equal(t, clients.ErrPasswordIsTooShort, err) } func Test_SetupCancelAtConfirmation(t *testing.T) { t.Parallel() retentionSecs := int64(duration.Week.Seconds()) params := setup.Params{ Username: "user", Password: "mysecretpassword", AuthToken: "mytoken", Org: "org", Bucket: "bucket", Retention: fmt.Sprintf("%ds", retentionSecs), Force: false, } ctrl := gomock.NewController(t) client := mock.NewMockSetupApi(ctrl) client.EXPECT().GetSetup(gomock.Any()).Return(api.ApiGetSetupRequest{ApiService: client}) client.EXPECT().GetSetupExecute(gomock.Any()).Return(api.InlineResponse200{Allowed: api.PtrBool(true)}, nil) configSvc := mock.NewMockConfigService(ctrl) configSvc.EXPECT().ListConfigs().Return(nil, nil) stdio := mock.NewMockStdIO(ctrl) stdio.EXPECT().Banner(gomock.Any()) stdio.EXPECT().GetConfirm(gomock.Any()).Return(false) cli := setup.Client{ CLI: clients.CLI{ConfigService: configSvc, ActiveConfig: config.Config{}, StdIO: stdio}, SetupApi: client, } err := cli.Setup(context.Background(), ¶ms) require.Equal(t, setup.ErrSetupCanceled, err) } func Test_SetupNonDefaultHost(t *testing.T) { t.Parallel() retentionSecs := int64(duration.Week.Seconds()) params := setup.Params{ Username: "user", Password: "mysecretpassword", AuthToken: "mytoken", Org: "org", Bucket: "bucket", Retention: fmt.Sprintf("%ds", retentionSecs), Force: true, ConfigName: "my-config", Host: "https://my-server.foo", } resp := api.OnboardingResponse{ Auth: &api.Authorization{Token: ¶ms.AuthToken}, Org: &api.Organization{Name: params.Org}, User: &api.UserResponse{Name: params.Username}, Bucket: &api.Bucket{Name: params.Bucket}, } ctrl := gomock.NewController(t) client := mock.NewMockSetupApi(ctrl) client.EXPECT().GetSetup(gomock.Any()).Return(api.ApiGetSetupRequest{ApiService: client}) client.EXPECT().GetSetupExecute(gomock.Any()).Return(api.InlineResponse200{Allowed: api.PtrBool(true)}, nil) client.EXPECT().PostSetup(gomock.Any()).Return(api.ApiPostSetupRequest{ApiService: client}) client.EXPECT().PostSetupExecute(tmock.MatchedBy(func(in api.ApiPostSetupRequest) bool { body := in.GetOnboardingRequest() return assert.NotNil(t, body) && assert.Equal(t, params.Username, body.Username) && assert.Equal(t, params.Password, *body.Password) && assert.Equal(t, params.AuthToken, *body.Token) && assert.Equal(t, params.Org, body.Org) && assert.Equal(t, params.Bucket, body.Bucket) && assert.Equal(t, retentionSecs, *body.RetentionPeriodSeconds) })).Return(resp, nil) configSvc := mock.NewMockConfigService(ctrl) configSvc.EXPECT().ListConfigs().Return(nil, nil) configSvc.EXPECT().CreateConfig(tmock.MatchedBy(func(in config.Config) bool { return assert.Equal(t, params.ConfigName, in.Name) && assert.Equal(t, params.AuthToken, in.Token) && assert.Equal(t, params.Host, in.Host) && assert.Equal(t, params.Org, in.Org) && assert.True(t, in.Active) })).DoAndReturn(func(in config.Config) (config.Config, error) { return in, nil }) stdio := mock.NewMockStdIO(ctrl) bytesWritten := bytes.Buffer{} stdio.EXPECT().Write(gomock.Any()).DoAndReturn(bytesWritten.Write).AnyTimes() cli := setup.Client{ CLI: clients.CLI{ConfigService: configSvc, ActiveConfig: config.Config{}, StdIO: stdio}, SetupApi: client, } require.NoError(t, cli.Setup(context.Background(), ¶ms)) testutils.MatchLines(t, []string{ `User\s+Organization\s+Bucket`, fmt.Sprintf(`%s\s+%s\s+%s`, params.Username, params.Org, params.Bucket), }, strings.Split(bytesWritten.String(), "\n")) }