diff --git a/services/collaboration/pkg/connector/connector_suite_test.go b/services/collaboration/pkg/connector/connector_suite_test.go new file mode 100644 index 000000000..0781160de --- /dev/null +++ b/services/collaboration/pkg/connector/connector_suite_test.go @@ -0,0 +1,13 @@ +package connector_test + +import ( + "testing" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" +) + +func TestGraph(t *testing.T) { + RegisterFailHandler(Fail) + RunSpecs(t, "Connector Suite") +} diff --git a/services/collaboration/pkg/connector/contentconnector_test.go b/services/collaboration/pkg/connector/contentconnector_test.go new file mode 100644 index 000000000..a39a29d58 --- /dev/null +++ b/services/collaboration/pkg/connector/contentconnector_test.go @@ -0,0 +1,418 @@ +package connector_test + +import ( + "context" + "errors" + "net/http" + "net/http/httptest" + "strings" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + "github.com/stretchr/testify/mock" + + appproviderv1beta1 "github.com/cs3org/go-cs3apis/cs3/app/provider/v1beta1" + gateway "github.com/cs3org/go-cs3apis/cs3/gateway/v1beta1" + userv1beta1 "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1" + providerv1beta1 "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1" + "github.com/cs3org/reva/v2/pkg/rgrpc/status" + "github.com/owncloud/ocis/v2/services/collaboration/pkg/config" + "github.com/owncloud/ocis/v2/services/collaboration/pkg/connector" + "github.com/owncloud/ocis/v2/services/collaboration/pkg/middleware" + + cs3mocks "github.com/cs3org/reva/v2/tests/cs3mocks/mocks" +) + +var _ = Describe("ContentConnector", func() { + var ( + cc *connector.ContentConnector + gatewayClient *cs3mocks.GatewayAPIClient + cfg *config.Config + wopiCtx middleware.WopiContext + + srv *httptest.Server + srvReqHeader http.Header + randomContent string + ) + + BeforeEach(func() { + // contentConnector only uses "cfg.CS3Api.DataGateway.Insecure", which is irrelevant for the tests + cfg = &config.Config{} + gatewayClient = &cs3mocks.GatewayAPIClient{} + cc = connector.NewContentConnector(gatewayClient, cfg) + + wopiCtx = middleware.WopiContext{ + AccessToken: "abcdef123456", + FileReference: providerv1beta1.Reference{ + ResourceId: &providerv1beta1.ResourceId{ + StorageId: "abc", + OpaqueId: "12345", + SpaceId: "zzz", + }, + Path: ".", + }, + User: &userv1beta1.User{}, // Not used for now + ViewMode: appproviderv1beta1.ViewMode_VIEW_MODE_READ_WRITE, + EditAppUrl: "http://test.ex.prv/edit", + ViewAppUrl: "http://test.ex.prv/view", + } + + randomContent = "This is the content of the test.txt file" + srv = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { + srvReqHeader = req.Header // save the request header to check later + switch req.URL.Path { + case "/download/failed.png": + w.WriteHeader(404) + case "/download/test.txt": + w.Write([]byte(randomContent)) + case "/upload/failed.png": + w.WriteHeader(404) + case "/upload/test.txt": + w.WriteHeader(200) + } + })) + }) + + AfterEach(func() { + srv.Close() + }) + + Describe("GetFile", func() { + It("No valid context", func() { + sb := &strings.Builder{} + ctx := context.Background() + err := cc.GetFile(ctx, sb) + Expect(err).To(HaveOccurred()) + }) + + It("Initiate download failed", func() { + sb := &strings.Builder{} + ctx := middleware.WopiContextToCtx(context.Background(), wopiCtx) + + targetErr := errors.New("Something went wrong") + gatewayClient.On("InitiateFileDownload", mock.Anything, mock.Anything).Times(1).Return(&gateway.InitiateFileDownloadResponse{ + Status: status.NewInternal(ctx, "Something failed"), + }, targetErr) + + err := cc.GetFile(ctx, sb) + Expect(err).To(Equal(targetErr)) + }) + + It("Initiate download status not ok", func() { + sb := &strings.Builder{} + ctx := middleware.WopiContextToCtx(context.Background(), wopiCtx) + + gatewayClient.On("InitiateFileDownload", mock.Anything, mock.Anything).Times(1).Return(&gateway.InitiateFileDownloadResponse{ + Status: status.NewInternal(ctx, "Something failed"), + }, nil) + + err := cc.GetFile(ctx, sb) + Expect(err).To(HaveOccurred()) + conErr := err.(*connector.ConnectorError) + Expect(conErr.HttpCodeOut).To(Equal(500)) + }) + + It("Missing download endpoint", func() { + sb := &strings.Builder{} + ctx := middleware.WopiContextToCtx(context.Background(), wopiCtx) + + gatewayClient.On("InitiateFileDownload", mock.Anything, mock.Anything).Times(1).Return(&gateway.InitiateFileDownloadResponse{ + Status: status.NewOK(ctx), + }, nil) + + err := cc.GetFile(ctx, sb) + Expect(err).To(HaveOccurred()) + conErr := err.(*connector.ConnectorError) + Expect(conErr.HttpCodeOut).To(Equal(500)) + }) + + It("Download request failed", func() { + sb := &strings.Builder{} + ctx := middleware.WopiContextToCtx(context.Background(), wopiCtx) + + gatewayClient.On("InitiateFileDownload", mock.Anything, mock.Anything).Times(1).Return(&gateway.InitiateFileDownloadResponse{ + Status: status.NewOK(ctx), + Protocols: []*gateway.FileDownloadProtocol{ + &gateway.FileDownloadProtocol{ + Protocol: "simple", + DownloadEndpoint: srv.URL + "/download/failed.png", + Token: "MyDownloadToken", + }, + }, + }, nil) + + err := cc.GetFile(ctx, sb) + Expect(srvReqHeader.Get("X-Access-Token")).To(Equal(wopiCtx.AccessToken)) + Expect(srvReqHeader.Get("X-Reva-Transfer")).To(Equal("MyDownloadToken")) + Expect(err).To(HaveOccurred()) + conErr := err.(*connector.ConnectorError) + Expect(conErr.HttpCodeOut).To(Equal(500)) + }) + + It("Download request success", func() { + sb := &strings.Builder{} + ctx := middleware.WopiContextToCtx(context.Background(), wopiCtx) + + gatewayClient.On("InitiateFileDownload", mock.Anything, mock.Anything).Times(1).Return(&gateway.InitiateFileDownloadResponse{ + Status: status.NewOK(ctx), + Protocols: []*gateway.FileDownloadProtocol{ + &gateway.FileDownloadProtocol{ + Protocol: "simple", + DownloadEndpoint: srv.URL + "/download/test.txt", + Token: "MyDownloadToken", + }, + }, + }, nil) + + err := cc.GetFile(ctx, sb) + Expect(srvReqHeader.Get("X-Access-Token")).To(Equal(wopiCtx.AccessToken)) + Expect(srvReqHeader.Get("X-Reva-Transfer")).To(Equal("MyDownloadToken")) + Expect(err).To(Succeed()) + Expect(sb.String()).To(Equal(randomContent)) + }) + }) + + Describe("PutFile", func() { + It("No valid context", func() { + reader := strings.NewReader("Content to upload is here!") + ctx := context.Background() + newLockId, err := cc.PutFile(ctx, reader, reader.Size(), "notARandomLockId") + Expect(err).To(HaveOccurred()) + Expect(newLockId).To(Equal("")) + }) + + It("Stat call failed", func() { + reader := strings.NewReader("Content to upload is here!") + ctx := middleware.WopiContextToCtx(context.Background(), wopiCtx) + + targetErr := errors.New("Something went wrong") + gatewayClient.On("Stat", mock.Anything, mock.Anything).Times(1).Return(&providerv1beta1.StatResponse{ + Status: status.NewInternal(ctx, "Something failed"), + }, targetErr) + + newLockId, err := cc.PutFile(ctx, reader, reader.Size(), "notARandomLockId") + Expect(err).To(Equal(targetErr)) + Expect(newLockId).To(Equal("")) + }) + + It("Stat call status not ok", func() { + reader := strings.NewReader("Content to upload is here!") + ctx := middleware.WopiContextToCtx(context.Background(), wopiCtx) + + gatewayClient.On("Stat", mock.Anything, mock.Anything).Times(1).Return(&providerv1beta1.StatResponse{ + Status: status.NewInternal(ctx, "Something failed"), + }, nil) + + newLockId, err := cc.PutFile(ctx, reader, reader.Size(), "notARandomLockId") + Expect(err).To(HaveOccurred()) + conErr := err.(*connector.ConnectorError) + Expect(conErr.HttpCodeOut).To(Equal(500)) + Expect(newLockId).To(Equal("")) + }) + + It("Mismatched lockId", func() { + reader := strings.NewReader("Content to upload is here!") + ctx := middleware.WopiContextToCtx(context.Background(), wopiCtx) + + gatewayClient.On("Stat", mock.Anything, mock.Anything).Times(1).Return(&providerv1beta1.StatResponse{ + Status: status.NewOK(ctx), + Info: &providerv1beta1.ResourceInfo{ + Lock: &providerv1beta1.Lock{ + LockId: "goodAndValidLock", + Type: providerv1beta1.LockType_LOCK_TYPE_WRITE, + }, + }, + }, nil) + + newLockId, err := cc.PutFile(ctx, reader, reader.Size(), "notARandomLockId") + Expect(err).To(HaveOccurred()) + conErr := err.(*connector.ConnectorError) + Expect(conErr.HttpCodeOut).To(Equal(409)) + Expect(newLockId).To(Equal("goodAndValidLock")) + }) + + It("Upload without lockId but on a non empty file", func() { + reader := strings.NewReader("Content to upload is here!") + ctx := middleware.WopiContextToCtx(context.Background(), wopiCtx) + + gatewayClient.On("Stat", mock.Anything, mock.Anything).Times(1).Return(&providerv1beta1.StatResponse{ + Status: status.NewOK(ctx), + Info: &providerv1beta1.ResourceInfo{ + Lock: nil, + Size: uint64(123456789), + }, + }, nil) + + newLockId, err := cc.PutFile(ctx, reader, reader.Size(), "") + Expect(err).To(HaveOccurred()) + conErr := err.(*connector.ConnectorError) + Expect(conErr.HttpCodeOut).To(Equal(409)) + Expect(newLockId).To(Equal("")) + }) + + It("Initiate upload fails", func() { + reader := strings.NewReader("Content to upload is here!") + ctx := middleware.WopiContextToCtx(context.Background(), wopiCtx) + + gatewayClient.On("Stat", mock.Anything, mock.Anything).Times(1).Return(&providerv1beta1.StatResponse{ + Status: status.NewOK(ctx), + Info: &providerv1beta1.ResourceInfo{ + Lock: &providerv1beta1.Lock{ + LockId: "goodAndValidLock", + Type: providerv1beta1.LockType_LOCK_TYPE_WRITE, + }, + Size: uint64(123456789), + }, + }, nil) + + targetErr := errors.New("Something went wrong") + gatewayClient.On("InitiateFileUpload", mock.Anything, mock.Anything).Times(1).Return(&gateway.InitiateFileUploadResponse{ + Status: status.NewInternal(ctx, "Something failed"), + }, targetErr) + + newLockId, err := cc.PutFile(ctx, reader, reader.Size(), "goodAndValidLock") + Expect(err).To(HaveOccurred()) + Expect(newLockId).To(Equal("")) + }) + + It("Initiate upload status not ok", func() { + reader := strings.NewReader("Content to upload is here!") + ctx := middleware.WopiContextToCtx(context.Background(), wopiCtx) + + gatewayClient.On("Stat", mock.Anything, mock.Anything).Times(1).Return(&providerv1beta1.StatResponse{ + Status: status.NewOK(ctx), + Info: &providerv1beta1.ResourceInfo{ + Lock: &providerv1beta1.Lock{ + LockId: "goodAndValidLock", + Type: providerv1beta1.LockType_LOCK_TYPE_WRITE, + }, + Size: uint64(123456789), + }, + }, nil) + + gatewayClient.On("InitiateFileUpload", mock.Anything, mock.Anything).Times(1).Return(&gateway.InitiateFileUploadResponse{ + Status: status.NewInternal(ctx, "Something failed"), + }, nil) + + newLockId, err := cc.PutFile(ctx, reader, reader.Size(), "goodAndValidLock") + Expect(err).To(HaveOccurred()) + conErr := err.(*connector.ConnectorError) + Expect(conErr.HttpCodeOut).To(Equal(500)) + Expect(newLockId).To(Equal("")) + }) + + It("Empty upload successful", func() { + reader := strings.NewReader("") + ctx := middleware.WopiContextToCtx(context.Background(), wopiCtx) + + gatewayClient.On("Stat", mock.Anything, mock.Anything).Times(1).Return(&providerv1beta1.StatResponse{ + Status: status.NewOK(ctx), + Info: &providerv1beta1.ResourceInfo{ + Lock: &providerv1beta1.Lock{ + LockId: "goodAndValidLock", + Type: providerv1beta1.LockType_LOCK_TYPE_WRITE, + }, + Size: uint64(123456789), + }, + }, nil) + + gatewayClient.On("InitiateFileUpload", mock.Anything, mock.Anything).Times(1).Return(&gateway.InitiateFileUploadResponse{ + Status: status.NewOK(ctx), + }, nil) + + newLockId, err := cc.PutFile(ctx, reader, reader.Size(), "goodAndValidLock") + Expect(err).To(Succeed()) + Expect(newLockId).To(Equal("")) + }) + + It("Missing upload endpoint", func() { + reader := strings.NewReader("Content to upload is here!") + ctx := middleware.WopiContextToCtx(context.Background(), wopiCtx) + + gatewayClient.On("Stat", mock.Anything, mock.Anything).Times(1).Return(&providerv1beta1.StatResponse{ + Status: status.NewOK(ctx), + Info: &providerv1beta1.ResourceInfo{ + Lock: &providerv1beta1.Lock{ + LockId: "goodAndValidLock", + Type: providerv1beta1.LockType_LOCK_TYPE_WRITE, + }, + Size: uint64(123456789), + }, + }, nil) + + gatewayClient.On("InitiateFileUpload", mock.Anything, mock.Anything).Times(1).Return(&gateway.InitiateFileUploadResponse{ + Status: status.NewOK(ctx), + }, nil) + + newLockId, err := cc.PutFile(ctx, reader, reader.Size(), "goodAndValidLock") + Expect(err).To(HaveOccurred()) + conErr := err.(*connector.ConnectorError) + Expect(conErr.HttpCodeOut).To(Equal(500)) + Expect(newLockId).To(Equal("")) + }) + + It("upload request failed", func() { + reader := strings.NewReader("Content to upload is here!") + ctx := middleware.WopiContextToCtx(context.Background(), wopiCtx) + + gatewayClient.On("Stat", mock.Anything, mock.Anything).Times(1).Return(&providerv1beta1.StatResponse{ + Status: status.NewOK(ctx), + Info: &providerv1beta1.ResourceInfo{ + Lock: &providerv1beta1.Lock{ + LockId: "goodAndValidLock", + Type: providerv1beta1.LockType_LOCK_TYPE_WRITE, + }, + Size: uint64(123456789), + }, + }, nil) + + gatewayClient.On("InitiateFileUpload", mock.Anything, mock.Anything).Times(1).Return(&gateway.InitiateFileUploadResponse{ + Status: status.NewOK(ctx), + Protocols: []*gateway.FileUploadProtocol{ + &gateway.FileUploadProtocol{ + Protocol: "simple", + UploadEndpoint: srv.URL + "/upload/failed.png", + }, + }, + }, nil) + + newLockId, err := cc.PutFile(ctx, reader, reader.Size(), "goodAndValidLock") + Expect(srvReqHeader.Get("X-Access-Token")).To(Equal(wopiCtx.AccessToken)) + Expect(err).To(HaveOccurred()) + conErr := err.(*connector.ConnectorError) + Expect(conErr.HttpCodeOut).To(Equal(500)) + Expect(newLockId).To(Equal("")) + }) + + It("upload request success", func() { + reader := strings.NewReader("Content to upload is here!") + ctx := middleware.WopiContextToCtx(context.Background(), wopiCtx) + + gatewayClient.On("Stat", mock.Anything, mock.Anything).Times(1).Return(&providerv1beta1.StatResponse{ + Status: status.NewOK(ctx), + Info: &providerv1beta1.ResourceInfo{ + Lock: &providerv1beta1.Lock{ + LockId: "goodAndValidLock", + Type: providerv1beta1.LockType_LOCK_TYPE_WRITE, + }, + Size: uint64(123456789), + }, + }, nil) + + gatewayClient.On("InitiateFileUpload", mock.Anything, mock.Anything).Times(1).Return(&gateway.InitiateFileUploadResponse{ + Status: status.NewOK(ctx), + Protocols: []*gateway.FileUploadProtocol{ + &gateway.FileUploadProtocol{ + Protocol: "simple", + UploadEndpoint: srv.URL + "/upload/test.txt", + }, + }, + }, nil) + + newLockId, err := cc.PutFile(ctx, reader, reader.Size(), "goodAndValidLock") + Expect(srvReqHeader.Get("X-Access-Token")).To(Equal(wopiCtx.AccessToken)) + Expect(err).To(Succeed()) + Expect(newLockId).To(Equal("")) + }) + }) +}) diff --git a/services/collaboration/pkg/connector/fileconnector.go b/services/collaboration/pkg/connector/fileconnector.go index 2366c4a43..6945f3b91 100644 --- a/services/collaboration/pkg/connector/fileconnector.go +++ b/services/collaboration/pkg/connector/fileconnector.go @@ -4,6 +4,7 @@ import ( "context" "encoding/hex" "path" + "strconv" "time" appproviderv1beta1 "github.com/cs3org/go-cs3apis/cs3/app/provider/v1beta1" @@ -69,6 +70,7 @@ func (f *FileConnector) GetLock(ctx context.Context) (string, error) { Str("StatusCode", resp.GetStatus().GetCode().String()). Str("StatusMsg", resp.GetStatus().GetMessage()). Msg("GetLock failed with unexpected status") + // TODO: Should we be more strict? There could be more causes for the failure return "", NewConnectorError(404, resp.GetStatus().GetCode().String()+" "+resp.GetStatus().GetMessage()) } @@ -469,7 +471,7 @@ func (f *FileConnector) CheckFileInfo(ctx context.Context) (FileInfo, error) { // OwnerId must use only alphanumeric chars (https://learn.microsoft.com/en-us/microsoft-365/cloud-storage-partner-program/rest/files/checkfileinfo/checkfileinfo-response#requirements-for-user-identity-properties) OwnerId: hex.EncodeToString([]byte(statRes.GetInfo().GetOwner().GetOpaqueId() + "@" + statRes.GetInfo().GetOwner().GetIdp())), Size: int64(statRes.GetInfo().GetSize()), - Version: statRes.GetInfo().GetMtime().String(), + Version: strconv.FormatUint(statRes.GetInfo().GetMtime().GetSeconds(), 10) + "." + strconv.FormatUint(uint64(statRes.GetInfo().GetMtime().GetNanos()), 10), BaseFileName: path.Base(statRes.GetInfo().GetPath()), BreadcrumbDocName: path.Base(statRes.GetInfo().GetPath()), // to get the folder we actually need to do a GetPath() request diff --git a/services/collaboration/pkg/connector/fileconnector_test.go b/services/collaboration/pkg/connector/fileconnector_test.go new file mode 100644 index 000000000..4ed54e2da --- /dev/null +++ b/services/collaboration/pkg/connector/fileconnector_test.go @@ -0,0 +1,875 @@ +package connector_test + +import ( + "context" + "encoding/hex" + "errors" + + appproviderv1beta1 "github.com/cs3org/go-cs3apis/cs3/app/provider/v1beta1" + userv1beta1 "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1" + providerv1beta1 "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1" + typesv1beta1 "github.com/cs3org/go-cs3apis/cs3/types/v1beta1" + "github.com/cs3org/reva/v2/pkg/rgrpc/status" + cs3mocks "github.com/cs3org/reva/v2/tests/cs3mocks/mocks" + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + "github.com/owncloud/ocis/v2/services/collaboration/pkg/config" + "github.com/owncloud/ocis/v2/services/collaboration/pkg/connector" + "github.com/owncloud/ocis/v2/services/collaboration/pkg/middleware" + "github.com/stretchr/testify/mock" +) + +var _ = Describe("FileConnector", func() { + var ( + fc *connector.FileConnector + gatewayClient *cs3mocks.GatewayAPIClient + cfg *config.Config + wopiCtx middleware.WopiContext + ) + + BeforeEach(func() { + cfg = &config.Config{ + App: config.App{ + LockName: "testName_for_unittests", // Only the LockName is used + }, + } + gatewayClient = &cs3mocks.GatewayAPIClient{} + fc = connector.NewFileConnector(gatewayClient, cfg) + + wopiCtx = middleware.WopiContext{ + AccessToken: "abcdef123456", + FileReference: providerv1beta1.Reference{ + ResourceId: &providerv1beta1.ResourceId{ + StorageId: "abc", + OpaqueId: "12345", + SpaceId: "zzz", + }, + Path: ".", + }, + User: &userv1beta1.User{ + Id: &userv1beta1.UserId{ + Idp: "inmemory", + OpaqueId: "opaqueId", + Type: userv1beta1.UserType_USER_TYPE_PRIMARY, + }, + Username: "Pet Shaft", + // Opaque is here for reference, not used by default but might be needed for some tests + //Opaque: &typesv1beta1.Opaque{ + // Map: map[string]*typesv1beta1.OpaqueEntry{ + // "public-share-role": &typesv1beta1.OpaqueEntry{ + // Decoder: "plain", + // Value: []byte("viewer"), + // }, + // }, + //}, + }, + ViewMode: appproviderv1beta1.ViewMode_VIEW_MODE_READ_WRITE, + EditAppUrl: "http://test.ex.prv/edit", + ViewAppUrl: "http://test.ex.prv/view", + } + }) + + Describe("GetLock", func() { + It("No valid context", func() { + ctx := context.Background() + newLockId, err := fc.GetLock(ctx) + Expect(err).To(HaveOccurred()) + Expect(newLockId).To(Equal("")) + }) + + It("Get lock failed", func() { + ctx := middleware.WopiContextToCtx(context.Background(), wopiCtx) + + targetErr := errors.New("Something went wrong") + gatewayClient.On("GetLock", mock.Anything, mock.Anything).Times(1).Return(&providerv1beta1.GetLockResponse{ + Status: status.NewInternal(ctx, "Something failed"), + }, targetErr) + + newLockId, err := fc.GetLock(ctx) + Expect(err).To(Equal(targetErr)) + Expect(newLockId).To(Equal("")) + }) + + It("Get lock failed status not ok", func() { + // assume failure happens because the target file doesn't exists + ctx := middleware.WopiContextToCtx(context.Background(), wopiCtx) + + gatewayClient.On("GetLock", mock.Anything, mock.Anything).Times(1).Return(&providerv1beta1.GetLockResponse{ + Status: status.NewNotFound(ctx, "File is missing"), + }, nil) + + newLockId, err := fc.GetLock(ctx) + Expect(err).To(HaveOccurred()) + conErr := err.(*connector.ConnectorError) + Expect(conErr.HttpCodeOut).To(Equal(404)) + Expect(newLockId).To(Equal("")) + }) + + It("Get lock success", func() { + // assume failure happens because the target file doesn't exists + ctx := middleware.WopiContextToCtx(context.Background(), wopiCtx) + + gatewayClient.On("GetLock", mock.Anything, mock.Anything).Times(1).Return(&providerv1beta1.GetLockResponse{ + Status: status.NewOK(ctx), + Lock: &providerv1beta1.Lock{ + LockId: "zzz999", + Type: providerv1beta1.LockType_LOCK_TYPE_WRITE, + }, + }, nil) + + newLockId, err := fc.GetLock(ctx) + Expect(err).To(Succeed()) + Expect(newLockId).To(Equal("zzz999")) + }) + }) + + Describe("Lock", func() { + Describe("Lock", func() { + It("No valid context", func() { + ctx := context.Background() + newLockId, err := fc.Lock(ctx, "newLock", "") + Expect(err).To(HaveOccurred()) + Expect(newLockId).To(Equal("")) + }) + + It("Empty lockId", func() { + ctx := middleware.WopiContextToCtx(context.Background(), wopiCtx) + + newLockId, err := fc.Lock(ctx, "", "") + Expect(err).To(HaveOccurred()) + conErr := err.(*connector.ConnectorError) + Expect(conErr.HttpCodeOut).To(Equal(400)) + Expect(newLockId).To(Equal("")) + }) + + It("Set lock failed", func() { + ctx := middleware.WopiContextToCtx(context.Background(), wopiCtx) + + targetErr := errors.New("Something went wrong") + gatewayClient.On("SetLock", mock.Anything, mock.Anything).Times(1).Return(&providerv1beta1.SetLockResponse{ + Status: status.NewInternal(ctx, "Something failed"), + }, targetErr) + + newLockId, err := fc.Lock(ctx, "abcdef123", "") + Expect(err).To(HaveOccurred()) + Expect(err).To(Equal(targetErr)) + Expect(newLockId).To(Equal("")) + }) + + It("Set lock success", func() { + ctx := middleware.WopiContextToCtx(context.Background(), wopiCtx) + + gatewayClient.On("SetLock", mock.Anything, mock.Anything).Times(1).Return(&providerv1beta1.SetLockResponse{ + Status: status.NewOK(ctx), + }, nil) + + newLockId, err := fc.Lock(ctx, "abcdef123", "") + Expect(err).To(Succeed()) + Expect(newLockId).To(Equal("")) + }) + + It("Set lock mismatches error getting lock", func() { + ctx := middleware.WopiContextToCtx(context.Background(), wopiCtx) + + gatewayClient.On("SetLock", mock.Anything, mock.Anything).Times(1).Return(&providerv1beta1.SetLockResponse{ + Status: status.NewFailedPrecondition(ctx, nil, "lock mismatch"), + }, nil) + + targetErr := errors.New("Something went wrong getting the lock") + gatewayClient.On("GetLock", mock.Anything, mock.Anything).Times(1).Return(&providerv1beta1.GetLockResponse{ + Status: status.NewInternal(ctx, "lock mismatch"), + }, targetErr) + + newLockId, err := fc.Lock(ctx, "abcdef123", "") + Expect(err).To(HaveOccurred()) + Expect(err).To(Equal(targetErr)) + Expect(newLockId).To(Equal("")) + }) + + It("Set lock mismatches", func() { + ctx := middleware.WopiContextToCtx(context.Background(), wopiCtx) + + gatewayClient.On("SetLock", mock.Anything, mock.Anything).Times(1).Return(&providerv1beta1.SetLockResponse{ + Status: status.NewFailedPrecondition(ctx, nil, "lock mismatch"), + }, nil) + + gatewayClient.On("GetLock", mock.Anything, mock.Anything).Times(1).Return(&providerv1beta1.GetLockResponse{ + Status: status.NewOK(ctx), + Lock: &providerv1beta1.Lock{ + LockId: "zzz999", + Type: providerv1beta1.LockType_LOCK_TYPE_WRITE, + }, + }, nil) + + newLockId, err := fc.Lock(ctx, "abcdef123", "") + Expect(err).To(HaveOccurred()) + conErr := err.(*connector.ConnectorError) + Expect(conErr.HttpCodeOut).To(Equal(409)) + Expect(newLockId).To(Equal("zzz999")) + }) + + It("Set lock mismatches but get lock matches", func() { + ctx := middleware.WopiContextToCtx(context.Background(), wopiCtx) + + gatewayClient.On("SetLock", mock.Anything, mock.Anything).Times(1).Return(&providerv1beta1.SetLockResponse{ + Status: status.NewFailedPrecondition(ctx, nil, "lock mismatch"), + }, nil) + + gatewayClient.On("GetLock", mock.Anything, mock.Anything).Times(1).Return(&providerv1beta1.GetLockResponse{ + Status: status.NewOK(ctx), + Lock: &providerv1beta1.Lock{ + LockId: "abcdef123", + Type: providerv1beta1.LockType_LOCK_TYPE_WRITE, + }, + }, nil) + + newLockId, err := fc.Lock(ctx, "abcdef123", "") + Expect(err).To(Succeed()) + Expect(newLockId).To(Equal("abcdef123")) + }) + + It("Set lock mismatches but get lock doesn't return lockId", func() { + ctx := middleware.WopiContextToCtx(context.Background(), wopiCtx) + + gatewayClient.On("SetLock", mock.Anything, mock.Anything).Times(1).Return(&providerv1beta1.SetLockResponse{ + Status: status.NewFailedPrecondition(ctx, nil, "lock mismatch"), + }, nil) + + gatewayClient.On("GetLock", mock.Anything, mock.Anything).Times(1).Return(&providerv1beta1.GetLockResponse{ + Status: status.NewOK(ctx), + }, nil) + + newLockId, err := fc.Lock(ctx, "abcdef123", "") + Expect(err).To(HaveOccurred()) + conErr := err.(*connector.ConnectorError) + Expect(conErr.HttpCodeOut).To(Equal(500)) + Expect(newLockId).To(Equal("")) + }) + + It("File not found", func() { + ctx := middleware.WopiContextToCtx(context.Background(), wopiCtx) + + gatewayClient.On("SetLock", mock.Anything, mock.Anything).Times(1).Return(&providerv1beta1.SetLockResponse{ + Status: status.NewNotFound(ctx, "file not found"), + }, nil) + + newLockId, err := fc.Lock(ctx, "abcdef123", "") + Expect(err).To(HaveOccurred()) + conErr := err.(*connector.ConnectorError) + Expect(conErr.HttpCodeOut).To(Equal(404)) + Expect(newLockId).To(Equal("")) + }) + + It("Default error handling (insufficient storage)", func() { + ctx := middleware.WopiContextToCtx(context.Background(), wopiCtx) + + gatewayClient.On("SetLock", mock.Anything, mock.Anything).Times(1).Return(&providerv1beta1.SetLockResponse{ + Status: status.NewInsufficientStorage(ctx, nil, "file too big"), + }, nil) + + newLockId, err := fc.Lock(ctx, "abcdef123", "") + Expect(err).To(HaveOccurred()) + conErr := err.(*connector.ConnectorError) + Expect(conErr.HttpCodeOut).To(Equal(500)) + Expect(newLockId).To(Equal("")) + }) + }) + + Describe("Unlock and relock", func() { + It("No valid context", func() { + ctx := context.Background() + newLockId, err := fc.Lock(ctx, "newLock", "oldLock") + Expect(err).To(HaveOccurred()) + Expect(newLockId).To(Equal("")) + }) + + It("Empty lockId", func() { + ctx := middleware.WopiContextToCtx(context.Background(), wopiCtx) + + newLockId, err := fc.Lock(ctx, "", "oldLock") + Expect(err).To(HaveOccurred()) + conErr := err.(*connector.ConnectorError) + Expect(conErr.HttpCodeOut).To(Equal(400)) + Expect(newLockId).To(Equal("")) + }) + + It("Refresh lock failed", func() { + ctx := middleware.WopiContextToCtx(context.Background(), wopiCtx) + + targetErr := errors.New("Something went wrong") + gatewayClient.On("RefreshLock", mock.Anything, mock.Anything).Times(1).Return(&providerv1beta1.RefreshLockResponse{ + Status: status.NewInternal(ctx, "Something failed"), + }, targetErr) + + newLockId, err := fc.Lock(ctx, "abcdef123", "oldLock") + Expect(err).To(HaveOccurred()) + Expect(err).To(Equal(targetErr)) + Expect(newLockId).To(Equal("")) + }) + + It("Refresh lock success", func() { + ctx := middleware.WopiContextToCtx(context.Background(), wopiCtx) + + gatewayClient.On("RefreshLock", mock.Anything, mock.Anything).Times(1).Return(&providerv1beta1.RefreshLockResponse{ + Status: status.NewOK(ctx), + }, nil) + + newLockId, err := fc.Lock(ctx, "abcdef123", "oldLock") + Expect(err).To(Succeed()) + Expect(newLockId).To(Equal("")) + }) + + It("Refresh lock mismatches error getting lock", func() { + ctx := middleware.WopiContextToCtx(context.Background(), wopiCtx) + + gatewayClient.On("RefreshLock", mock.Anything, mock.Anything).Times(1).Return(&providerv1beta1.RefreshLockResponse{ + Status: status.NewConflict(ctx, nil, "lock mismatch"), + }, nil) + + targetErr := errors.New("Something went wrong getting the lock") + gatewayClient.On("GetLock", mock.Anything, mock.Anything).Times(1).Return(&providerv1beta1.GetLockResponse{ + Status: status.NewInternal(ctx, "lock mismatch"), + }, targetErr) + + newLockId, err := fc.Lock(ctx, "abcdef123", "112233") + Expect(err).To(HaveOccurred()) + Expect(err).To(Equal(targetErr)) + Expect(newLockId).To(Equal("")) + }) + + It("Refresh lock mismatches", func() { + ctx := middleware.WopiContextToCtx(context.Background(), wopiCtx) + + gatewayClient.On("RefreshLock", mock.Anything, mock.Anything).Times(1).Return(&providerv1beta1.RefreshLockResponse{ + Status: status.NewConflict(ctx, nil, "lock mismatch"), + }, nil) + + gatewayClient.On("GetLock", mock.Anything, mock.Anything).Times(1).Return(&providerv1beta1.GetLockResponse{ + Status: status.NewOK(ctx), + Lock: &providerv1beta1.Lock{ + LockId: "zzz999", + Type: providerv1beta1.LockType_LOCK_TYPE_WRITE, + }, + }, nil) + + newLockId, err := fc.Lock(ctx, "abcdef123", "112233") + Expect(err).To(HaveOccurred()) + conErr := err.(*connector.ConnectorError) + Expect(conErr.HttpCodeOut).To(Equal(409)) + Expect(newLockId).To(Equal("zzz999")) + }) + + It("Refresh lock mismatches but get lock matches", func() { + ctx := middleware.WopiContextToCtx(context.Background(), wopiCtx) + + gatewayClient.On("RefreshLock", mock.Anything, mock.Anything).Times(1).Return(&providerv1beta1.RefreshLockResponse{ + Status: status.NewConflict(ctx, nil, "lock mismatch"), + }, nil) + + gatewayClient.On("GetLock", mock.Anything, mock.Anything).Times(1).Return(&providerv1beta1.GetLockResponse{ + Status: status.NewOK(ctx), + Lock: &providerv1beta1.Lock{ + LockId: "abcdef123", + Type: providerv1beta1.LockType_LOCK_TYPE_WRITE, + }, + }, nil) + + newLockId, err := fc.Lock(ctx, "abcdef123", "112233") + Expect(err).To(Succeed()) + Expect(newLockId).To(Equal("abcdef123")) + }) + + It("Refresh lock mismatches but get lock doesn't return lockId", func() { + ctx := middleware.WopiContextToCtx(context.Background(), wopiCtx) + + gatewayClient.On("RefreshLock", mock.Anything, mock.Anything).Times(1).Return(&providerv1beta1.RefreshLockResponse{ + Status: status.NewConflict(ctx, nil, "lock mismatch"), + }, nil) + + gatewayClient.On("GetLock", mock.Anything, mock.Anything).Times(1).Return(&providerv1beta1.GetLockResponse{ + Status: status.NewOK(ctx), + }, nil) + + newLockId, err := fc.Lock(ctx, "abcdef123", "112233") + Expect(err).To(HaveOccurred()) + conErr := err.(*connector.ConnectorError) + Expect(conErr.HttpCodeOut).To(Equal(500)) + Expect(newLockId).To(Equal("")) + }) + + It("File not found", func() { + ctx := middleware.WopiContextToCtx(context.Background(), wopiCtx) + + gatewayClient.On("RefreshLock", mock.Anything, mock.Anything).Times(1).Return(&providerv1beta1.RefreshLockResponse{ + Status: status.NewNotFound(ctx, "file not found"), + }, nil) + + newLockId, err := fc.Lock(ctx, "abcdef123", "112233") + Expect(err).To(HaveOccurred()) + conErr := err.(*connector.ConnectorError) + Expect(conErr.HttpCodeOut).To(Equal(404)) + Expect(newLockId).To(Equal("")) + }) + + It("Default error handling (insufficient storage)", func() { + ctx := middleware.WopiContextToCtx(context.Background(), wopiCtx) + + gatewayClient.On("RefreshLock", mock.Anything, mock.Anything).Times(1).Return(&providerv1beta1.RefreshLockResponse{ + Status: status.NewInsufficientStorage(ctx, nil, "file too big"), + }, nil) + + newLockId, err := fc.Lock(ctx, "abcdef123", "112233") + Expect(err).To(HaveOccurred()) + conErr := err.(*connector.ConnectorError) + Expect(conErr.HttpCodeOut).To(Equal(500)) + Expect(newLockId).To(Equal("")) + }) + }) + }) + + Describe("RefreshLock", func() { + It("No valid context", func() { + ctx := context.Background() + newLockId, err := fc.RefreshLock(ctx, "newLock") + Expect(err).To(HaveOccurred()) + Expect(newLockId).To(Equal("")) + }) + + It("Empty lockId", func() { + ctx := middleware.WopiContextToCtx(context.Background(), wopiCtx) + + newLockId, err := fc.RefreshLock(ctx, "") + Expect(err).To(HaveOccurred()) + conErr := err.(*connector.ConnectorError) + Expect(conErr.HttpCodeOut).To(Equal(400)) + Expect(newLockId).To(Equal("")) + }) + + It("Refresh lock fails", func() { + ctx := middleware.WopiContextToCtx(context.Background(), wopiCtx) + + targetErr := errors.New("Something went wrong") + gatewayClient.On("RefreshLock", mock.Anything, mock.Anything).Times(1).Return(&providerv1beta1.RefreshLockResponse{ + Status: status.NewConflict(ctx, nil, "lock mismatch"), + }, targetErr) + + newLockId, err := fc.RefreshLock(ctx, "abcdef123") + Expect(err).To(HaveOccurred()) + Expect(err).To(Equal(targetErr)) + Expect(newLockId).To(Equal("")) + }) + + It("Refresh lock success", func() { + ctx := middleware.WopiContextToCtx(context.Background(), wopiCtx) + + gatewayClient.On("RefreshLock", mock.Anything, mock.Anything).Times(1).Return(&providerv1beta1.RefreshLockResponse{ + Status: status.NewOK(ctx), + }, nil) + + newLockId, err := fc.RefreshLock(ctx, "abcdef123") + Expect(err).To(Succeed()) + Expect(newLockId).To(Equal("")) + }) + + It("Refresh lock file not found", func() { + ctx := middleware.WopiContextToCtx(context.Background(), wopiCtx) + + gatewayClient.On("RefreshLock", mock.Anything, mock.Anything).Times(1).Return(&providerv1beta1.RefreshLockResponse{ + Status: status.NewNotFound(ctx, "file not found"), + }, nil) + + newLockId, err := fc.RefreshLock(ctx, "abcdef123") + Expect(err).To(HaveOccurred()) + conErr := err.(*connector.ConnectorError) + Expect(conErr.HttpCodeOut).To(Equal(404)) + Expect(newLockId).To(Equal("")) + }) + + It("Refresh lock mismatch and get lock fails", func() { + ctx := middleware.WopiContextToCtx(context.Background(), wopiCtx) + + gatewayClient.On("RefreshLock", mock.Anything, mock.Anything).Times(1).Return(&providerv1beta1.RefreshLockResponse{ + Status: status.NewConflict(ctx, nil, "lock mismatch"), + }, nil) + + targetErr := errors.New("Something went wrong") + gatewayClient.On("GetLock", mock.Anything, mock.Anything).Times(1).Return(&providerv1beta1.GetLockResponse{ + Status: status.NewConflict(ctx, nil, "lock mismatch"), + }, targetErr) + + newLockId, err := fc.RefreshLock(ctx, "abcdef123") + Expect(err).To(HaveOccurred()) + Expect(err).To(Equal(targetErr)) + Expect(newLockId).To(Equal("")) + }) + + It("Refresh lock mismatch and get lock status not ok", func() { + ctx := middleware.WopiContextToCtx(context.Background(), wopiCtx) + + gatewayClient.On("RefreshLock", mock.Anything, mock.Anything).Times(1).Return(&providerv1beta1.RefreshLockResponse{ + Status: status.NewConflict(ctx, nil, "lock mismatch"), + }, nil) + + gatewayClient.On("GetLock", mock.Anything, mock.Anything).Times(1).Return(&providerv1beta1.GetLockResponse{ + Status: status.NewInternal(ctx, "lock mismatch"), + }, nil) + + newLockId, err := fc.RefreshLock(ctx, "abcdef123") + Expect(err).To(HaveOccurred()) + conErr := err.(*connector.ConnectorError) + Expect(conErr.HttpCodeOut).To(Equal(500)) + Expect(newLockId).To(Equal("")) + }) + + It("Refresh lock mismatch and no lock", func() { + ctx := middleware.WopiContextToCtx(context.Background(), wopiCtx) + + gatewayClient.On("RefreshLock", mock.Anything, mock.Anything).Times(1).Return(&providerv1beta1.RefreshLockResponse{ + Status: status.NewConflict(ctx, nil, "lock mismatch"), + }, nil) + + gatewayClient.On("GetLock", mock.Anything, mock.Anything).Times(1).Return(&providerv1beta1.GetLockResponse{ + Status: status.NewOK(ctx), + }, nil) + + newLockId, err := fc.RefreshLock(ctx, "abcdef123") + Expect(err).To(HaveOccurred()) + conErr := err.(*connector.ConnectorError) + Expect(conErr.HttpCodeOut).To(Equal(409)) + Expect(newLockId).To(Equal("")) + }) + + It("Refresh lock mismatch", func() { + ctx := middleware.WopiContextToCtx(context.Background(), wopiCtx) + + gatewayClient.On("RefreshLock", mock.Anything, mock.Anything).Times(1).Return(&providerv1beta1.RefreshLockResponse{ + Status: status.NewConflict(ctx, nil, "lock mismatch"), + }, nil) + + gatewayClient.On("GetLock", mock.Anything, mock.Anything).Times(1).Return(&providerv1beta1.GetLockResponse{ + Status: status.NewOK(ctx), + Lock: &providerv1beta1.Lock{ + LockId: "zzz999", + Type: providerv1beta1.LockType_LOCK_TYPE_WRITE, + }, + }, nil) + + newLockId, err := fc.RefreshLock(ctx, "abcdef123") + Expect(err).To(HaveOccurred()) + conErr := err.(*connector.ConnectorError) + Expect(conErr.HttpCodeOut).To(Equal(409)) + Expect(newLockId).To(Equal("zzz999")) + }) + + It("Default error handling (insufficient storage)", func() { + ctx := middleware.WopiContextToCtx(context.Background(), wopiCtx) + + gatewayClient.On("RefreshLock", mock.Anything, mock.Anything).Times(1).Return(&providerv1beta1.RefreshLockResponse{ + Status: status.NewInsufficientStorage(ctx, nil, "file too big"), + }, nil) + + newLockId, err := fc.RefreshLock(ctx, "abcdef123") + Expect(err).To(HaveOccurred()) + conErr := err.(*connector.ConnectorError) + Expect(conErr.HttpCodeOut).To(Equal(500)) + Expect(newLockId).To(Equal("")) + }) + }) + + Describe("Unlock", func() { + It("No valid context", func() { + ctx := context.Background() + newLockId, err := fc.UnLock(ctx, "newLock") + Expect(err).To(HaveOccurred()) + Expect(newLockId).To(Equal("")) + }) + + It("Empty lockId", func() { + ctx := middleware.WopiContextToCtx(context.Background(), wopiCtx) + + newLockId, err := fc.UnLock(ctx, "") + Expect(err).To(HaveOccurred()) + conErr := err.(*connector.ConnectorError) + Expect(conErr.HttpCodeOut).To(Equal(400)) + Expect(newLockId).To(Equal("")) + }) + + It("Unlock fails", func() { + ctx := middleware.WopiContextToCtx(context.Background(), wopiCtx) + + targetErr := errors.New("Something went wrong") + gatewayClient.On("Unlock", mock.Anything, mock.Anything).Times(1).Return(&providerv1beta1.UnlockResponse{ + Status: status.NewInternal(ctx, "something failed"), + }, targetErr) + + newLockId, err := fc.UnLock(ctx, "abcdef123") + Expect(err).To(HaveOccurred()) + Expect(err).To(Equal(targetErr)) + Expect(newLockId).To(Equal("")) + }) + + It("Unlock success", func() { + ctx := middleware.WopiContextToCtx(context.Background(), wopiCtx) + + gatewayClient.On("Unlock", mock.Anything, mock.Anything).Times(1).Return(&providerv1beta1.UnlockResponse{ + Status: status.NewOK(ctx), + }, nil) + + newLockId, err := fc.UnLock(ctx, "abcdef123") + Expect(err).To(Succeed()) + Expect(newLockId).To(Equal("")) + }) + + It("Unlock file isn't locked", func() { + ctx := middleware.WopiContextToCtx(context.Background(), wopiCtx) + + gatewayClient.On("Unlock", mock.Anything, mock.Anything).Times(1).Return(&providerv1beta1.UnlockResponse{ + Status: status.NewConflict(ctx, nil, "lock mismatch"), + }, nil) + + newLockId, err := fc.UnLock(ctx, "abcdef123") + Expect(err).To(HaveOccurred()) + conErr := err.(*connector.ConnectorError) + Expect(conErr.HttpCodeOut).To(Equal(409)) + Expect(newLockId).To(Equal("")) + }) + + It("Unlock mismatch get lock fails", func() { + ctx := middleware.WopiContextToCtx(context.Background(), wopiCtx) + + gatewayClient.On("Unlock", mock.Anything, mock.Anything).Times(1).Return(&providerv1beta1.UnlockResponse{ + Status: status.NewLocked(ctx, "lock mismatch"), + }, nil) + + targetErr := errors.New("Something went wrong") + gatewayClient.On("GetLock", mock.Anything, mock.Anything).Times(1).Return(&providerv1beta1.GetLockResponse{ + Status: status.NewInternal(ctx, "something failed"), + }, targetErr) + + newLockId, err := fc.UnLock(ctx, "abcdef123") + Expect(err).To(HaveOccurred()) + Expect(err).To(Equal(targetErr)) + Expect(newLockId).To(Equal("")) + }) + + It("Unlock mismatch get lock status not ok", func() { + ctx := middleware.WopiContextToCtx(context.Background(), wopiCtx) + + gatewayClient.On("Unlock", mock.Anything, mock.Anything).Times(1).Return(&providerv1beta1.UnlockResponse{ + Status: status.NewLocked(ctx, "lock mismatch"), + }, nil) + + gatewayClient.On("GetLock", mock.Anything, mock.Anything).Times(1).Return(&providerv1beta1.GetLockResponse{ + Status: status.NewInternal(ctx, "something failed"), + }, nil) + + newLockId, err := fc.UnLock(ctx, "abcdef123") + Expect(err).To(HaveOccurred()) + conErr := err.(*connector.ConnectorError) + Expect(conErr.HttpCodeOut).To(Equal(500)) + Expect(newLockId).To(Equal("")) + }) + + It("Unlock mismatch get lock doesn't return lock", func() { + ctx := middleware.WopiContextToCtx(context.Background(), wopiCtx) + + gatewayClient.On("Unlock", mock.Anything, mock.Anything).Times(1).Return(&providerv1beta1.UnlockResponse{ + Status: status.NewLocked(ctx, "lock mismatch"), + }, nil) + + gatewayClient.On("GetLock", mock.Anything, mock.Anything).Times(1).Return(&providerv1beta1.GetLockResponse{ + Status: status.NewOK(ctx), + }, nil) + + newLockId, err := fc.UnLock(ctx, "abcdef123") + Expect(err).To(HaveOccurred()) + conErr := err.(*connector.ConnectorError) + Expect(conErr.HttpCodeOut).To(Equal(409)) + Expect(newLockId).To(Equal("")) + }) + + It("Unlock mismatch", func() { + ctx := middleware.WopiContextToCtx(context.Background(), wopiCtx) + + gatewayClient.On("Unlock", mock.Anything, mock.Anything).Times(1).Return(&providerv1beta1.UnlockResponse{ + Status: status.NewLocked(ctx, "lock mismatch"), + }, nil) + + gatewayClient.On("GetLock", mock.Anything, mock.Anything).Times(1).Return(&providerv1beta1.GetLockResponse{ + Status: status.NewOK(ctx), + Lock: &providerv1beta1.Lock{ + LockId: "zzz999", + Type: providerv1beta1.LockType_LOCK_TYPE_WRITE, + }, + }, nil) + + newLockId, err := fc.UnLock(ctx, "abcdef123") + Expect(err).To(HaveOccurred()) + conErr := err.(*connector.ConnectorError) + Expect(conErr.HttpCodeOut).To(Equal(409)) + Expect(newLockId).To(Equal("zzz999")) + }) + + It("Default error handling (insufficient storage)", func() { + ctx := middleware.WopiContextToCtx(context.Background(), wopiCtx) + + gatewayClient.On("Unlock", mock.Anything, mock.Anything).Times(1).Return(&providerv1beta1.UnlockResponse{ + Status: status.NewInsufficientStorage(ctx, nil, "file too big"), + }, nil) + + newLockId, err := fc.UnLock(ctx, "abcdef123") + Expect(err).To(HaveOccurred()) + conErr := err.(*connector.ConnectorError) + Expect(conErr.HttpCodeOut).To(Equal(500)) + Expect(newLockId).To(Equal("")) + }) + }) + + Describe("CheckFileInfo", func() { + It("No valid context", func() { + ctx := context.Background() + newFileInfo, err := fc.CheckFileInfo(ctx) + Expect(err).To(HaveOccurred()) + Expect(newFileInfo).To(Equal(connector.FileInfo{})) + }) + + It("Stat fails", func() { + ctx := middleware.WopiContextToCtx(context.Background(), wopiCtx) + + targetErr := errors.New("Something went wrong") + gatewayClient.On("Stat", mock.Anything, mock.Anything).Times(1).Return(&providerv1beta1.StatResponse{ + Status: status.NewInternal(ctx, "something failed"), + }, targetErr) + + newFileInfo, err := fc.CheckFileInfo(ctx) + Expect(err).To(HaveOccurred()) + Expect(err).To(Equal(targetErr)) + Expect(newFileInfo).To(Equal(connector.FileInfo{})) + }) + + It("Stat fails status not ok", func() { + ctx := middleware.WopiContextToCtx(context.Background(), wopiCtx) + + gatewayClient.On("Stat", mock.Anything, mock.Anything).Times(1).Return(&providerv1beta1.StatResponse{ + Status: status.NewInternal(ctx, "something failed"), + }, nil) + + newFileInfo, err := fc.CheckFileInfo(ctx) + Expect(err).To(HaveOccurred()) + conErr := err.(*connector.ConnectorError) + Expect(conErr.HttpCodeOut).To(Equal(500)) + Expect(newFileInfo).To(Equal(connector.FileInfo{})) + }) + + It("Stat success", func() { + ctx := middleware.WopiContextToCtx(context.Background(), wopiCtx) + + gatewayClient.On("Stat", mock.Anything, mock.Anything).Times(1).Return(&providerv1beta1.StatResponse{ + Status: status.NewOK(ctx), + Info: &providerv1beta1.ResourceInfo{ + Owner: &userv1beta1.UserId{ + Idp: "customIdp", + OpaqueId: "aabbcc", + Type: userv1beta1.UserType_USER_TYPE_PRIMARY, + }, + Size: uint64(998877), + Mtime: &typesv1beta1.Timestamp{ + Seconds: uint64(16273849), + }, + Path: "/path/to/test.txt", + // Other properties aren't used for now. + }, + }, nil) + + expectedFileInfo := connector.FileInfo{ + OwnerId: "61616262636340637573746f6d496470", // hex of aabbcc@customIdp + Size: int64(998877), + Version: "16273849.0", + BaseFileName: "test.txt", + BreadcrumbDocName: "test.txt", + UserCanNotWriteRelative: true, + HostViewUrl: "http://test.ex.prv/view", + HostEditUrl: "http://test.ex.prv/edit", + EnableOwnerTermination: false, + SupportsExtendedLockLength: true, + SupportsGetLock: true, + SupportsLocks: true, + SupportsUpdate: true, + UserCanWrite: true, + UserId: "6f7061717565496440696e6d656d6f7279", // hex of opaqueId@inmemory + UserFriendlyName: "Pet Shaft", + } + + newFileInfo, err := fc.CheckFileInfo(ctx) + Expect(err).To(Succeed()) + Expect(newFileInfo).To(Equal(expectedFileInfo)) + }) + + It("Stat success guests", func() { + // add user's opaque to include public-share-role + wopiCtx.User.Opaque = &typesv1beta1.Opaque{ + Map: map[string]*typesv1beta1.OpaqueEntry{ + "public-share-role": &typesv1beta1.OpaqueEntry{ + Decoder: "plain", + Value: []byte("viewer"), + }, + }, + } + // change view mode to view only + wopiCtx.ViewMode = appproviderv1beta1.ViewMode_VIEW_MODE_VIEW_ONLY + + ctx := middleware.WopiContextToCtx(context.Background(), wopiCtx) + + gatewayClient.On("Stat", mock.Anything, mock.Anything).Times(1).Return(&providerv1beta1.StatResponse{ + Status: status.NewOK(ctx), + Info: &providerv1beta1.ResourceInfo{ + Owner: &userv1beta1.UserId{ + Idp: "customIdp", + OpaqueId: "aabbcc", + Type: userv1beta1.UserType_USER_TYPE_PRIMARY, + }, + Size: uint64(998877), + Mtime: &typesv1beta1.Timestamp{ + Seconds: uint64(16273849), + }, + Path: "/path/to/test.txt", + // Other properties aren't used for now. + }, + }, nil) + + expectedFileInfo := connector.FileInfo{ + OwnerId: "61616262636340637573746f6d496470", // hex of aabbcc@customIdp + Size: int64(998877), + Version: "16273849.0", + BaseFileName: "test.txt", + BreadcrumbDocName: "test.txt", + UserCanNotWriteRelative: true, + HostViewUrl: "http://test.ex.prv/view", + HostEditUrl: "http://test.ex.prv/edit", + EnableOwnerTermination: false, + SupportsExtendedLockLength: true, + SupportsGetLock: true, + SupportsLocks: true, + DisableExport: true, + DisableCopy: true, + DisablePrint: true, + IsAnonymousUser: true, + UserId: "guest-zzz000", + UserFriendlyName: "guest zzz000", + } + + newFileInfo, err := fc.CheckFileInfo(ctx) + + // UserId and UserFriendlyName have random Ids generated which are impossible to guess + // Check both separately + Expect(newFileInfo.UserId).To(HavePrefix(hex.EncodeToString([]byte("guest-")))) + Expect(newFileInfo.UserFriendlyName).To(HavePrefix("Guest ")) + // overwrite UserId and UserFriendlyName here for easier matching + newFileInfo.UserId = "guest-zzz000" + newFileInfo.UserFriendlyName = "guest zzz000" + + Expect(err).To(Succeed()) + Expect(newFileInfo).To(Equal(expectedFileInfo)) + }) + }) +}) diff --git a/services/collaboration/pkg/helpers/discovery_test.go b/services/collaboration/pkg/helpers/discovery_test.go new file mode 100644 index 000000000..cb1679045 --- /dev/null +++ b/services/collaboration/pkg/helpers/discovery_test.go @@ -0,0 +1,124 @@ +package helpers_test + +import ( + "net/http" + "net/http/httptest" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + + "github.com/owncloud/ocis/v2/ocis-pkg/log" + "github.com/owncloud/ocis/v2/services/collaboration/pkg/config" + "github.com/owncloud/ocis/v2/services/collaboration/pkg/helpers" +) + +var _ = Describe("Discovery", func() { + var ( + discoveryContent1 string + srv *httptest.Server + ) + + BeforeEach(func() { + discoveryContent1 = ` + + + + + + + + + + + + + + + + + + + + + + + + + + + +` + srv = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { + switch req.URL.Path { + case "/bad/hosting/discovery": + w.WriteHeader(500) + case "/good/hosting/discovery": + w.Write([]byte(discoveryContent1)) + case "/wrongformat/hosting/discovery": + w.Write([]byte("Text that be XML /form