diff --git a/cmd/ghcs/delete.go b/cmd/ghcs/delete.go index b4c0bcbcf..0c21c1674 100644 --- a/cmd/ghcs/delete.go +++ b/cmd/ghcs/delete.go @@ -28,14 +28,16 @@ type deleteOptions struct { prompter prompter } -//go:generate moq -fmt goimports -rm -out mock_prompter.go . prompter +//go:generate moq -fmt goimports -rm -skip-ensure -out mock_prompter.go . prompter type prompter interface { Confirm(message string) (bool, error) } -//go:generate moq -fmt goimports -rm -out mock_api.go . apiClient +//go:generate moq -fmt goimports -rm -skip-ensure -out mock_api.go . apiClient type apiClient interface { GetUser(ctx context.Context) (*api.User, error) + GetCodespaceToken(ctx context.Context, user, name string) (string, error) + GetCodespace(ctx context.Context, token, user, name string) (*api.Codespace, error) ListCodespaces(ctx context.Context, user string) ([]*api.Codespace, error) DeleteCodespace(ctx context.Context, user, name string) error } @@ -80,18 +82,34 @@ func delete(ctx context.Context, log logger, opts deleteOptions) error { return fmt.Errorf("error getting user: %w", err) } - codespaces, err := opts.apiClient.ListCodespaces(ctx, user.Login) - if err != nil { - return fmt.Errorf("error getting codespaces: %w", err) - } - + var codespaces []*api.Codespace nameFilter := opts.codespaceName - if nameFilter == "" && !opts.deleteAll && opts.repoFilter == "" { - c, err := chooseCodespaceFromList(ctx, codespaces) + if nameFilter == "" { + codespaces, err = opts.apiClient.ListCodespaces(ctx, user.Login) if err != nil { - return fmt.Errorf("error choosing codespace: %w", err) + return fmt.Errorf("error getting codespaces: %w", err) } - nameFilter = c.Name + + if !opts.deleteAll && opts.repoFilter == "" { + c, err := chooseCodespaceFromList(ctx, codespaces) + if err != nil { + return fmt.Errorf("error choosing codespace: %w", err) + } + nameFilter = c.Name + } + } else { + // TODO: this token is discarded and then re-requested later in DeleteCodespace + token, err := opts.apiClient.GetCodespaceToken(ctx, user.Login, nameFilter) + if err != nil { + return fmt.Errorf("error getting codespace token: %w", err) + } + + codespace, err := opts.apiClient.GetCodespace(ctx, token, user.Login, nameFilter) + if err != nil { + return fmt.Errorf("error fetching codespace information: %w", err) + } + + codespaces = []*api.Codespace{codespace} } codespacesToDelete := make([]*api.Codespace, 0, len(codespaces)) @@ -112,7 +130,7 @@ func delete(ctx context.Context, log logger, opts deleteOptions) error { continue } } - if nameFilter == "" || !opts.skipConfirm { + if !opts.skipConfirm { confirmed, err := confirmDeletion(opts.prompter, c, opts.isInteractive) if err != nil { return fmt.Errorf("unable to confirm: %w", err) @@ -133,7 +151,7 @@ func delete(ctx context.Context, log logger, opts deleteOptions) error { codespaceName := c.Name g.Go(func() error { if err := opts.apiClient.DeleteCodespace(ctx, user.Login, codespaceName); err != nil { - log.Errorf("error deleting codespace %q: %v", codespaceName, err) + _, _ = log.Errorf("error deleting codespace %q: %v", codespaceName, err) return err } return nil diff --git a/cmd/ghcs/delete_test.go b/cmd/ghcs/delete_test.go index beb371dd4..7d90a3bd2 100644 --- a/cmd/ghcs/delete_test.go +++ b/cmd/ghcs/delete_test.go @@ -31,9 +31,6 @@ func TestDelete(t *testing.T) { codespaceName: "hubot-robawt-abc", }, codespaces: []*api.Codespace{ - { - Name: "monalisa-spoonknife-123", - }, { Name: "hubot-robawt-abc", }, @@ -130,12 +127,6 @@ func TestDelete(t *testing.T) { GetUserFunc: func(_ context.Context) (*api.User, error) { return user, nil }, - ListCodespacesFunc: func(_ context.Context, userLogin string) ([]*api.Codespace, error) { - if userLogin != user.Login { - return nil, fmt.Errorf("unexpected user %q", userLogin) - } - return tt.codespaces, nil - }, DeleteCodespaceFunc: func(_ context.Context, userLogin, name string) error { if userLogin != user.Login { return fmt.Errorf("unexpected user %q", userLogin) @@ -143,6 +134,30 @@ func TestDelete(t *testing.T) { return nil }, } + if tt.opts.codespaceName == "" { + apiMock.ListCodespacesFunc = func(_ context.Context, userLogin string) ([]*api.Codespace, error) { + if userLogin != user.Login { + return nil, fmt.Errorf("unexpected user %q", userLogin) + } + return tt.codespaces, nil + } + } else { + apiMock.GetCodespaceTokenFunc = func(_ context.Context, userLogin, name string) (string, error) { + if userLogin != user.Login { + return "", fmt.Errorf("unexpected user %q", userLogin) + } + return "CS_TOKEN", nil + } + apiMock.GetCodespaceFunc = func(_ context.Context, token, userLogin, name string) (*api.Codespace, error) { + if userLogin != user.Login { + return nil, fmt.Errorf("unexpected user %q", userLogin) + } + if token != "CS_TOKEN" { + return nil, fmt.Errorf("unexpected token %q", token) + } + return tt.codespaces[0], nil + } + } opts := tt.opts opts.apiClient = apiMock opts.now = func() time.Time { return now } diff --git a/cmd/ghcs/mock_api.go b/cmd/ghcs/mock_api.go index 46edd2835..256a30ec3 100644 --- a/cmd/ghcs/mock_api.go +++ b/cmd/ghcs/mock_api.go @@ -10,10 +10,6 @@ import ( "github.com/github/ghcs/internal/api" ) -// Ensure, that apiClientMock does implement apiClient. -// If this is not the case, regenerate this file with moq. -var _ apiClient = &apiClientMock{} - // apiClientMock is a mock implementation of apiClient. // // func TestSomethingThatUsesapiClient(t *testing.T) { @@ -23,6 +19,12 @@ var _ apiClient = &apiClientMock{} // DeleteCodespaceFunc: func(ctx context.Context, user string, name string) error { // panic("mock out the DeleteCodespace method") // }, +// GetCodespaceFunc: func(ctx context.Context, token string, user string, name string) (*api.Codespace, error) { +// panic("mock out the GetCodespace method") +// }, +// GetCodespaceTokenFunc: func(ctx context.Context, user string, name string) (string, error) { +// panic("mock out the GetCodespaceToken method") +// }, // GetUserFunc: func(ctx context.Context) (*api.User, error) { // panic("mock out the GetUser method") // }, @@ -39,6 +41,12 @@ type apiClientMock struct { // DeleteCodespaceFunc mocks the DeleteCodespace method. DeleteCodespaceFunc func(ctx context.Context, user string, name string) error + // GetCodespaceFunc mocks the GetCodespace method. + GetCodespaceFunc func(ctx context.Context, token string, user string, name string) (*api.Codespace, error) + + // GetCodespaceTokenFunc mocks the GetCodespaceToken method. + GetCodespaceTokenFunc func(ctx context.Context, user string, name string) (string, error) + // GetUserFunc mocks the GetUser method. GetUserFunc func(ctx context.Context) (*api.User, error) @@ -56,6 +64,26 @@ type apiClientMock struct { // Name is the name argument value. Name string } + // GetCodespace holds details about calls to the GetCodespace method. + GetCodespace []struct { + // Ctx is the ctx argument value. + Ctx context.Context + // Token is the token argument value. + Token string + // User is the user argument value. + User string + // Name is the name argument value. + Name string + } + // GetCodespaceToken holds details about calls to the GetCodespaceToken method. + GetCodespaceToken []struct { + // Ctx is the ctx argument value. + Ctx context.Context + // User is the user argument value. + User string + // Name is the name argument value. + Name string + } // GetUser holds details about calls to the GetUser method. GetUser []struct { // Ctx is the ctx argument value. @@ -69,9 +97,11 @@ type apiClientMock struct { User string } } - lockDeleteCodespace sync.RWMutex - lockGetUser sync.RWMutex - lockListCodespaces sync.RWMutex + lockDeleteCodespace sync.RWMutex + lockGetCodespace sync.RWMutex + lockGetCodespaceToken sync.RWMutex + lockGetUser sync.RWMutex + lockListCodespaces sync.RWMutex } // DeleteCodespace calls DeleteCodespaceFunc. @@ -113,6 +143,88 @@ func (mock *apiClientMock) DeleteCodespaceCalls() []struct { return calls } +// GetCodespace calls GetCodespaceFunc. +func (mock *apiClientMock) GetCodespace(ctx context.Context, token string, user string, name string) (*api.Codespace, error) { + if mock.GetCodespaceFunc == nil { + panic("apiClientMock.GetCodespaceFunc: method is nil but apiClient.GetCodespace was just called") + } + callInfo := struct { + Ctx context.Context + Token string + User string + Name string + }{ + Ctx: ctx, + Token: token, + User: user, + Name: name, + } + mock.lockGetCodespace.Lock() + mock.calls.GetCodespace = append(mock.calls.GetCodespace, callInfo) + mock.lockGetCodespace.Unlock() + return mock.GetCodespaceFunc(ctx, token, user, name) +} + +// GetCodespaceCalls gets all the calls that were made to GetCodespace. +// Check the length with: +// len(mockedapiClient.GetCodespaceCalls()) +func (mock *apiClientMock) GetCodespaceCalls() []struct { + Ctx context.Context + Token string + User string + Name string +} { + var calls []struct { + Ctx context.Context + Token string + User string + Name string + } + mock.lockGetCodespace.RLock() + calls = mock.calls.GetCodespace + mock.lockGetCodespace.RUnlock() + return calls +} + +// GetCodespaceToken calls GetCodespaceTokenFunc. +func (mock *apiClientMock) GetCodespaceToken(ctx context.Context, user string, name string) (string, error) { + if mock.GetCodespaceTokenFunc == nil { + panic("apiClientMock.GetCodespaceTokenFunc: method is nil but apiClient.GetCodespaceToken was just called") + } + callInfo := struct { + Ctx context.Context + User string + Name string + }{ + Ctx: ctx, + User: user, + Name: name, + } + mock.lockGetCodespaceToken.Lock() + mock.calls.GetCodespaceToken = append(mock.calls.GetCodespaceToken, callInfo) + mock.lockGetCodespaceToken.Unlock() + return mock.GetCodespaceTokenFunc(ctx, user, name) +} + +// GetCodespaceTokenCalls gets all the calls that were made to GetCodespaceToken. +// Check the length with: +// len(mockedapiClient.GetCodespaceTokenCalls()) +func (mock *apiClientMock) GetCodespaceTokenCalls() []struct { + Ctx context.Context + User string + Name string +} { + var calls []struct { + Ctx context.Context + User string + Name string + } + mock.lockGetCodespaceToken.RLock() + calls = mock.calls.GetCodespaceToken + mock.lockGetCodespaceToken.RUnlock() + return calls +} + // GetUser calls GetUserFunc. func (mock *apiClientMock) GetUser(ctx context.Context) (*api.User, error) { if mock.GetUserFunc == nil { diff --git a/cmd/ghcs/mock_prompter.go b/cmd/ghcs/mock_prompter.go index e15209c03..56581b64d 100644 --- a/cmd/ghcs/mock_prompter.go +++ b/cmd/ghcs/mock_prompter.go @@ -7,10 +7,6 @@ import ( "sync" ) -// Ensure, that prompterMock does implement prompter. -// If this is not the case, regenerate this file with moq. -var _ prompter = &prompterMock{} - // prompterMock is a mock implementation of prompter. // // func TestSomethingThatUsesprompter(t *testing.T) {