diff --git a/cmd/src/batch_remote.go b/cmd/src/batch_remote.go index 483e6e8db9..25768f8052 100644 --- a/cmd/src/batch_remote.go +++ b/cmd/src/batch_remote.go @@ -79,7 +79,7 @@ Examples: ui.ResolvingNamespaceSuccess(namespace.ID) ui.SendingBatchChange() - batchChangeName, err := svc.UpsertBatchChange(ctx, spec.Name, namespace.ID) + batchChangeID, batchChangeName, err := svc.UpsertBatchChange(ctx, spec.Name, namespace.ID) if err != nil { return err } @@ -93,6 +93,7 @@ Examples: flags.allowIgnored, flags.allowUnsupported, flags.clearCache, + batchChangeID, ) if err != nil { return err diff --git a/go.mod b/go.mod index 923bcc71fb..d4a955e26d 100644 --- a/go.mod +++ b/go.mod @@ -93,6 +93,7 @@ require ( github.com/spf13/cobra v1.4.0 // indirect github.com/spf13/pflag v1.0.5 // indirect github.com/ssor/bom v0.0.0-20170718123548-6386211fdfcf // indirect + github.com/stretchr/objx v0.4.0 // indirect github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect github.com/xeipuuv/gojsonschema v1.2.0 // indirect diff --git a/go.sum b/go.sum index 25ba1fefe8..85775bb7cc 100644 --- a/go.sum +++ b/go.sum @@ -390,6 +390,7 @@ github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DM github.com/ssor/bom v0.0.0-20170718123548-6386211fdfcf h1:pvbZ0lM0XWPBqUKqFU8cmavspvIl9nulOYwdy6IFRRo= github.com/ssor/bom v0.0.0-20170718123548-6386211fdfcf/go.mod h1:RJID2RhlZKId02nZ62WenDCkgHFerpIOmW0iT7GKmXM= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0 h1:M2gUjqZET1qApGOWNSnZ49BAIMX4F/1plDv3+l31EJ4= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= diff --git a/internal/api/mock/api.go b/internal/api/mock/api.go new file mode 100644 index 0000000000..43e7f795a7 --- /dev/null +++ b/internal/api/mock/api.go @@ -0,0 +1,78 @@ +package mock + +import ( + "context" + "encoding/json" + "io" + "net/http" + + "github.com/sourcegraph/src-cli/internal/api" + "github.com/stretchr/testify/mock" +) + +type Client struct { + mock.Mock +} + +func (m *Client) NewQuery(query string) api.Request { + args := m.Called(query) + return args.Get(0).(api.Request) +} + +func (m *Client) NewRequest(query string, vars map[string]interface{}) api.Request { + args := m.Called(query, vars) + return args.Get(0).(api.Request) +} + +func (m *Client) NewGzippedRequest(query string, vars map[string]interface{}) api.Request { + args := m.Called(query, vars) + return args.Get(0).(api.Request) +} + +func (m *Client) NewGzippedQuery(query string) api.Request { + args := m.Called(query) + return args.Get(0).(api.Request) +} + +func (m *Client) NewHTTPRequest(ctx context.Context, method, path string, body io.Reader) (*http.Request, error) { + args := m.Called(ctx, method, path, body) + var obj *http.Request + if args.Get(0) != nil { + obj = args.Get(0).(*http.Request) + } + return obj, args.Error(1) +} + +func (m *Client) Do(req *http.Request) (*http.Response, error) { + args := m.Called(req) + var obj *http.Response + if args.Get(0) != nil { + obj = args.Get(0).(*http.Response) + } + return obj, args.Error(1) +} + +type Request struct { + mock.Mock + Response string +} + +func (r *Request) Do(ctx context.Context, result interface{}) (bool, error) { + args := r.Called(ctx, result) + if r.Response != "" { + if err := json.Unmarshal([]byte(r.Response), result); err != nil { + return false, err + } + } + return args.Bool(0), args.Error(1) +} + +func (r *Request) DoRaw(ctx context.Context, result interface{}) (bool, error) { + args := r.Called(ctx, result) + if r.Response != "" { + if err := json.Unmarshal([]byte(r.Response), result); err != nil { + return false, err + } + } + return args.Bool(0), args.Error(1) +} diff --git a/internal/batches/service/remote.go b/internal/batches/service/remote.go index 87733240b8..6d4577812b 100644 --- a/internal/batches/service/remote.go +++ b/internal/batches/service/remote.go @@ -17,6 +17,7 @@ mutation UpsertEmptyBatchChange( name: $name, namespace: $namespace ) { + id name } } @@ -26,9 +27,10 @@ func (svc *Service) UpsertBatchChange( ctx context.Context, name string, namespaceID string, -) (string, error) { +) (string, string, error) { var resp struct { UpsertEmptyBatchChange struct { + ID string `json:"id"` Name string `json:"name"` } `json:"upsertEmptyBatchChange"` } @@ -37,10 +39,10 @@ func (svc *Service) UpsertBatchChange( "name": name, "namespace": namespaceID, }).Do(ctx, &resp); err != nil || !ok { - return "", err + return "", "", err } - return resp.UpsertEmptyBatchChange.Name, nil + return resp.UpsertEmptyBatchChange.ID, resp.UpsertEmptyBatchChange.Name, nil } const createBatchSpecFromRawQuery = ` @@ -50,6 +52,7 @@ mutation CreateBatchSpecFromRaw( $allowIgnored: Boolean!, $allowUnsupported: Boolean!, $noCache: Boolean!, + $batchChange: ID!, ) { createBatchSpecFromRaw( batchSpec: $batchSpec, @@ -57,6 +60,7 @@ mutation CreateBatchSpecFromRaw( allowIgnored: $allowIgnored, allowUnsupported: $allowUnsupported, noCache: $noCache, + batchChange: $batchChange, ) { id } @@ -70,6 +74,7 @@ func (svc *Service) CreateBatchSpecFromRaw( allowIgnored bool, allowUnsupported bool, noCache bool, + batchChange string, ) (string, error) { var resp struct { CreateBatchSpecFromRaw struct { @@ -83,6 +88,7 @@ func (svc *Service) CreateBatchSpecFromRaw( "allowIgnored": allowIgnored, "allowUnsupported": allowUnsupported, "noCache": noCache, + "batchChange": batchChange, }).Do(ctx, &resp); err != nil || !ok { return "", err } diff --git a/internal/batches/service/remote_test.go b/internal/batches/service/remote_test.go new file mode 100644 index 0000000000..6cbfecaa2b --- /dev/null +++ b/internal/batches/service/remote_test.go @@ -0,0 +1,193 @@ +package service_test + +import ( + "context" + "encoding/json" + "testing" + + mockclient "github.com/sourcegraph/src-cli/internal/api/mock" + "github.com/sourcegraph/src-cli/internal/batches/service" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" + + "github.com/sourcegraph/sourcegraph/lib/errors" +) + +func TestService_UpsertBatchChange(t *testing.T) { + client := new(mockclient.Client) + mockRequest := new(mockclient.Request) + svc := service.New(&service.Opts{Client: client}) + + tests := []struct { + name string + + mockInvokes func() + + requestName string + requestNamespaceID string + + expectedID string + expectedName string + expectedErr error + }{ + { + name: "New Batch Change", + mockInvokes: func() { + client.On("NewRequest", mock.Anything, map[string]interface{}{ + "name": "my-change", + "namespace": "my-namespace", + }). + Return(mockRequest, nil). + Once() + mockRequest.On("Do", mock.Anything, mock.Anything). + Run(func(args mock.Arguments) { + json.Unmarshal([]byte(`{"upsertEmptyBatchChange":{"id":"123", "name":"my-change"}}`), &args[1]) + }). + Return(true, nil). + Once() + }, + requestName: "my-change", + requestNamespaceID: "my-namespace", + expectedID: "123", + expectedName: "my-change", + }, + { + name: "Failed to upsert batch change", + mockInvokes: func() { + client.On("NewRequest", mock.Anything, map[string]interface{}{ + "name": "my-change", + "namespace": "my-namespace", + }). + Return(mockRequest, nil). + Once() + mockRequest.On("Do", mock.Anything, mock.Anything). + Return(false, errors.New("did not get a good response code")). + Once() + }, + requestName: "my-change", + requestNamespaceID: "my-namespace", + expectedErr: errors.New("did not get a good response code"), + }, + } + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + if test.mockInvokes != nil { + test.mockInvokes() + } + + id, name, err := svc.UpsertBatchChange(context.Background(), test.requestName, test.requestNamespaceID) + assert.Equal(t, test.expectedID, id) + assert.Equal(t, test.expectedName, name) + if test.expectedErr != nil { + assert.Error(t, err) + assert.Equal(t, test.expectedErr.Error(), err.Error()) + } else { + assert.NoError(t, err) + } + + client.AssertExpectations(t) + }) + } +} + +func TestService_CreateBatchSpecFromRaw(t *testing.T) { + client := new(mockclient.Client) + mockRequest := new(mockclient.Request) + svc := service.New(&service.Opts{Client: client}) + + tests := []struct { + name string + + mockInvokes func() + + requestBatchSpec string + requestNamespaceID string + requestAllowIgnored bool + requestAllowUnsupported bool + requestNoCache bool + requestBatchChange string + + expectedID string + expectedErr error + }{ + { + name: "Create batch spec", + mockInvokes: func() { + client.On("NewRequest", mock.Anything, map[string]interface{}{ + "batchSpec": "abc", + "namespace": "some-namespace", + "allowIgnored": false, + "allowUnsupported": false, + "noCache": false, + "batchChange": "123", + }). + Return(mockRequest, nil). + Once() + mockRequest.On("Do", mock.Anything, mock.Anything). + Run(func(args mock.Arguments) { + json.Unmarshal([]byte(`{"createBatchSpecFromRaw":{"id":"xyz"}}`), &args[1]) + }). + Return(true, nil). + Once() + }, + requestBatchSpec: "abc", + requestNamespaceID: "some-namespace", + requestAllowIgnored: false, + requestAllowUnsupported: false, + requestNoCache: false, + requestBatchChange: "123", + expectedID: "xyz", + }, + { + name: "Failed to create batch spec", + mockInvokes: func() { + client.On("NewRequest", mock.Anything, map[string]interface{}{ + "batchSpec": "abc", + "namespace": "some-namespace", + "allowIgnored": false, + "allowUnsupported": false, + "noCache": false, + "batchChange": "123", + }). + Return(mockRequest, nil). + Once() + mockRequest.On("Do", mock.Anything, mock.Anything). + Return(false, errors.New("did not get a good response code")). + Once() + }, + requestBatchSpec: "abc", + requestNamespaceID: "some-namespace", + requestAllowIgnored: false, + requestAllowUnsupported: false, + requestNoCache: false, + requestBatchChange: "123", + expectedErr: errors.New("did not get a good response code"), + }, + } + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + if test.mockInvokes != nil { + test.mockInvokes() + } + + id, err := svc.CreateBatchSpecFromRaw( + context.Background(), + test.requestBatchSpec, + test.requestNamespaceID, + test.requestAllowIgnored, + test.requestAllowUnsupported, + test.requestNoCache, + test.requestBatchChange, + ) + assert.Equal(t, test.expectedID, id) + if test.expectedErr != nil { + assert.Error(t, err) + assert.Equal(t, test.expectedErr.Error(), err.Error()) + } else { + assert.NoError(t, err) + } + + client.AssertExpectations(t) + }) + } +}