github.com/cs3org/reva/v2@v2.27.7/pkg/storage/utils/decomposedfs/upload_async_test.go (about)

     1  package decomposedfs
     2  
     3  import (
     4  	"bytes"
     5  	"context"
     6  	"io"
     7  	"os"
     8  	"path/filepath"
     9  
    10  	userpb "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1"
    11  	cs3permissions "github.com/cs3org/go-cs3apis/cs3/permissions/v1beta1"
    12  	v1beta11 "github.com/cs3org/go-cs3apis/cs3/rpc/v1beta1"
    13  	provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1"
    14  	ruser "github.com/cs3org/reva/v2/pkg/ctx"
    15  	"github.com/cs3org/reva/v2/pkg/events"
    16  	"github.com/cs3org/reva/v2/pkg/events/stream"
    17  	"github.com/cs3org/reva/v2/pkg/rgrpc/todo/pool"
    18  	"github.com/cs3org/reva/v2/pkg/storage"
    19  	"github.com/cs3org/reva/v2/pkg/storage/cache"
    20  	"github.com/cs3org/reva/v2/pkg/storage/utils/decomposedfs/aspects"
    21  	"github.com/cs3org/reva/v2/pkg/storage/utils/decomposedfs/lookup"
    22  	"github.com/cs3org/reva/v2/pkg/storage/utils/decomposedfs/metadata"
    23  	"github.com/cs3org/reva/v2/pkg/storage/utils/decomposedfs/node"
    24  	"github.com/cs3org/reva/v2/pkg/storage/utils/decomposedfs/options"
    25  	"github.com/cs3org/reva/v2/pkg/storage/utils/decomposedfs/permissions"
    26  	"github.com/cs3org/reva/v2/pkg/storage/utils/decomposedfs/permissions/mocks"
    27  	"github.com/cs3org/reva/v2/pkg/storage/utils/decomposedfs/timemanager"
    28  	"github.com/cs3org/reva/v2/pkg/storage/utils/decomposedfs/tree"
    29  	treemocks "github.com/cs3org/reva/v2/pkg/storage/utils/decomposedfs/tree/mocks"
    30  	"github.com/cs3org/reva/v2/pkg/storagespace"
    31  	"github.com/cs3org/reva/v2/pkg/store"
    32  	"github.com/cs3org/reva/v2/pkg/utils"
    33  	"github.com/cs3org/reva/v2/tests/helpers"
    34  	"github.com/rs/zerolog"
    35  	"github.com/stretchr/testify/mock"
    36  	"google.golang.org/grpc"
    37  
    38  	. "github.com/onsi/ginkgo/v2"
    39  	. "github.com/onsi/gomega"
    40  )
    41  
    42  var _ = Describe("Async file uploads", Ordered, func() {
    43  	var (
    44  		ref = &provider.Reference{
    45  			ResourceId: &provider.ResourceId{
    46  				SpaceId: "u-s-e-r-id",
    47  			},
    48  			Path: "/foo",
    49  		}
    50  
    51  		rootRef = &provider.Reference{
    52  			ResourceId: &provider.ResourceId{
    53  				SpaceId:  "u-s-e-r-id",
    54  				OpaqueId: "u-s-e-r-id",
    55  			},
    56  			Path: "/",
    57  		}
    58  
    59  		user = &userpb.User{
    60  			Id: &userpb.UserId{
    61  				Idp:      "idp",
    62  				OpaqueId: "u-s-e-r-id",
    63  				Type:     userpb.UserType_USER_TYPE_PRIMARY,
    64  			},
    65  			Username: "username",
    66  		}
    67  
    68  		firstContent  = []byte("0123456789")
    69  		secondContent = []byte("01234567890123456789")
    70  
    71  		ctx = ruser.ContextSetUser(context.Background(), user)
    72  
    73  		pub      chan interface{}
    74  		con      chan interface{}
    75  		uploadID string
    76  
    77  		fs                   storage.FS
    78  		o                    *options.Options
    79  		lu                   *lookup.Lookup
    80  		pmock                *mocks.PermissionsChecker
    81  		cs3permissionsclient *mocks.CS3PermissionsClient
    82  		permissionsSelector  pool.Selectable[cs3permissions.PermissionsAPIClient]
    83  		bs                   *treemocks.Blobstore
    84  
    85  		succeedPostprocessing = func(uploadID string) {
    86  			// finish postprocessing
    87  			con <- events.PostprocessingFinished{
    88  				UploadID: uploadID,
    89  				Outcome:  events.PPOutcomeContinue,
    90  			}
    91  			// wait for upload to be ready
    92  			ev, ok := (<-pub).(events.UploadReady)
    93  			Expect(ok).To(BeTrue())
    94  			Expect(ev.Failed).To(BeFalse())
    95  		}
    96  
    97  		failPostprocessing = func(uploadID string, outcome events.PostprocessingOutcome) {
    98  			// finish postprocessing
    99  			con <- events.PostprocessingFinished{
   100  				UploadID: uploadID,
   101  				Outcome:  outcome,
   102  			}
   103  			// wait for upload to be ready
   104  			ev, ok := (<-pub).(events.UploadReady)
   105  			Expect(ok).To(BeTrue())
   106  			Expect(ev.Failed).To(BeTrue())
   107  		}
   108  
   109  		fileStatus = func() (bool, string, int) {
   110  			// check processing status
   111  			resources, err := fs.ListFolder(ctx, rootRef, []string{}, []string{})
   112  			Expect(err).ToNot(HaveOccurred())
   113  			Expect(len(resources)).To(BeElementOf([2]int{0, 1}), "should not have more than one child")
   114  
   115  			item := resources[0]
   116  			Expect(item.Path).To(Equal(ref.Path))
   117  			return len(resources) == 1, utils.ReadPlainFromOpaque(item.Opaque, "status"), int(item.GetSize())
   118  		}
   119  		parentSize = func() int {
   120  			parentInfo, err := fs.GetMD(ctx, rootRef, []string{}, []string{})
   121  			Expect(err).ToNot(HaveOccurred())
   122  			return int(parentInfo.Size)
   123  		}
   124  		revisionCount = func() int {
   125  			revisions, err := fs.ListRevisions(ctx, ref)
   126  			Expect(err).ToNot(HaveOccurred())
   127  			return len(revisions)
   128  		}
   129  	)
   130  
   131  	BeforeEach(func() {
   132  		// setup test
   133  		tmpRoot, err := helpers.TempDir("reva-unit-tests-*-root")
   134  		Expect(err).ToNot(HaveOccurred())
   135  
   136  		o, err = options.New(map[string]interface{}{
   137  			"root":                tmpRoot,
   138  			"asyncfileuploads":    true,
   139  			"treetime_accounting": true,
   140  			"treesize_accounting": true,
   141  		})
   142  		Expect(err).ToNot(HaveOccurred())
   143  
   144  		lu = lookup.New(metadata.NewXattrsBackend(o.Root, cache.Config{}), o, &timemanager.Manager{})
   145  		pmock = &mocks.PermissionsChecker{}
   146  
   147  		cs3permissionsclient = &mocks.CS3PermissionsClient{}
   148  		pool.RemoveSelector("PermissionsSelector" + "any")
   149  		permissionsSelector = pool.GetSelector[cs3permissions.PermissionsAPIClient](
   150  			"PermissionsSelector",
   151  			"any",
   152  			func(cc grpc.ClientConnInterface) cs3permissions.PermissionsAPIClient {
   153  				return cs3permissionsclient
   154  			},
   155  		)
   156  		bs = &treemocks.Blobstore{}
   157  
   158  		// create space uses CheckPermission endpoint
   159  		cs3permissionsclient.On("CheckPermission", mock.Anything, mock.Anything, mock.Anything).Return(&cs3permissions.CheckPermissionResponse{
   160  			Status: &v1beta11.Status{Code: v1beta11.Code_CODE_OK},
   161  		}, nil).Times(1)
   162  
   163  		// for this test we don't care about permissions
   164  		pmock.On("AssemblePermissions", mock.Anything, mock.Anything).
   165  			Return(&provider.ResourcePermissions{
   166  				Stat:               true,
   167  				GetQuota:           true,
   168  				InitiateFileUpload: true,
   169  				ListContainer:      true,
   170  				ListFileVersions:   true,
   171  			}, nil)
   172  
   173  		// setup fs
   174  		pub, con = make(chan interface{}), make(chan interface{})
   175  		tree := tree.New(lu, bs, o, store.Create(), &zerolog.Logger{})
   176  
   177  		aspects := aspects.Aspects{
   178  			Lookup:      lu,
   179  			Tree:        tree,
   180  			Permissions: permissions.NewPermissions(pmock, permissionsSelector),
   181  			EventStream: stream.Chan{pub, con},
   182  			Trashbin:    &DecomposedfsTrashbin{},
   183  		}
   184  		fs, err = New(o, aspects, &zerolog.Logger{})
   185  		Expect(err).ToNot(HaveOccurred())
   186  
   187  		resp, err := fs.CreateStorageSpace(ctx, &provider.CreateStorageSpaceRequest{Owner: user, Type: "personal"})
   188  		Expect(err).ToNot(HaveOccurred())
   189  		Expect(resp.Status.Code).To(Equal(v1beta11.Code_CODE_OK))
   190  		resID, err := storagespace.ParseID(resp.StorageSpace.Id.OpaqueId)
   191  		Expect(err).ToNot(HaveOccurred())
   192  		ref.ResourceId = &resID
   193  
   194  		bs.On("Upload", mock.AnythingOfType("*node.Node"), mock.AnythingOfType("string"), mock.Anything).
   195  			Return(nil).
   196  			Run(func(args mock.Arguments) {
   197  				n := args.Get(0).(*node.Node)
   198  				data, err := os.ReadFile(args.Get(1).(string))
   199  				Expect(err).ToNot(HaveOccurred())
   200  				Expect(len(data)).To(Equal(int(n.Blobsize)))
   201  			})
   202  
   203  		// start upload of a file
   204  		uploadIds, err := fs.InitiateUpload(ctx, ref, 10, map[string]string{})
   205  		Expect(err).ToNot(HaveOccurred())
   206  		Expect(len(uploadIds)).To(Equal(2))
   207  		Expect(uploadIds["simple"]).ToNot(BeEmpty())
   208  		Expect(uploadIds["tus"]).ToNot(BeEmpty())
   209  
   210  		uploadRef := &provider.Reference{Path: "/" + uploadIds["simple"]}
   211  
   212  		_, err = fs.Upload(ctx, storage.UploadRequest{
   213  			Ref:    uploadRef,
   214  			Body:   io.NopCloser(bytes.NewReader(firstContent)),
   215  			Length: int64(len(firstContent)),
   216  		}, nil)
   217  		Expect(err).ToNot(HaveOccurred())
   218  
   219  		uploadID = uploadIds["simple"]
   220  
   221  		// wait for bytes received event
   222  		_, ok := (<-pub).(events.BytesReceived)
   223  		Expect(ok).To(BeTrue())
   224  
   225  		// blobstore not called yet
   226  		bs.AssertNumberOfCalls(GinkgoT(), "Upload", 0)
   227  	})
   228  
   229  	AfterEach(func() {
   230  		if o.Root != "" {
   231  			os.RemoveAll(o.Root)
   232  		}
   233  		close(pub)
   234  		close(con)
   235  	})
   236  
   237  	When("the uploaded file is new", func() {
   238  		It("succeeds eventually", func() {
   239  			// node is created
   240  			resources, err := fs.ListFolder(ctx, rootRef, []string{}, []string{})
   241  			Expect(err).ToNot(HaveOccurred())
   242  			Expect(len(resources)).To(Equal(1))
   243  
   244  			item := resources[0]
   245  			Expect(item.Path).To(Equal(ref.Path))
   246  			Expect(utils.ReadPlainFromOpaque(item.Opaque, "status")).To(Equal("processing"))
   247  
   248  			succeedPostprocessing(uploadID)
   249  
   250  			// blobstore called now
   251  			bs.AssertNumberOfCalls(GinkgoT(), "Upload", 1)
   252  
   253  			// node ready
   254  			resources, err = fs.ListFolder(ctx, rootRef, []string{}, []string{})
   255  			Expect(err).ToNot(HaveOccurred())
   256  			Expect(len(resources)).To(Equal(1))
   257  
   258  			item = resources[0]
   259  			Expect(item.Path).To(Equal(ref.Path))
   260  			Expect(utils.ReadPlainFromOpaque(item.Opaque, "status")).To(BeEmpty())
   261  
   262  		})
   263  
   264  		It("deletes node and bytes when instructed", func() {
   265  			// node is created
   266  			resources, err := fs.ListFolder(ctx, rootRef, []string{}, []string{})
   267  			Expect(err).ToNot(HaveOccurred())
   268  			Expect(len(resources)).To(Equal(1))
   269  
   270  			item := resources[0]
   271  			Expect(item.Path).To(Equal(ref.Path))
   272  			Expect(utils.ReadPlainFromOpaque(item.Opaque, "status")).To(Equal("processing"))
   273  
   274  			// bytes are in dedicated path
   275  			_, err = os.Stat(filepath.Join(o.Root, "uploads", uploadID))
   276  			Expect(err).To(BeNil())
   277  
   278  			failPostprocessing(uploadID, events.PPOutcomeDelete)
   279  
   280  			// blobstore still not called now
   281  			bs.AssertNumberOfCalls(GinkgoT(), "Upload", 0)
   282  
   283  			// node gone
   284  			resources, err = fs.ListFolder(ctx, rootRef, []string{}, []string{})
   285  			Expect(err).ToNot(HaveOccurred())
   286  			Expect(len(resources)).To(Equal(0))
   287  
   288  			// bytes gone
   289  			_, err = os.Stat(filepath.Join(o.Root, "uploads", uploadID))
   290  			Expect(err).ToNot(BeNil())
   291  		})
   292  
   293  		It("deletes node and keeps the bytes when instructed", func() {
   294  			// node is created
   295  			resources, err := fs.ListFolder(ctx, rootRef, []string{}, []string{})
   296  			Expect(err).ToNot(HaveOccurred())
   297  			Expect(len(resources)).To(Equal(1))
   298  
   299  			item := resources[0]
   300  			Expect(item.Path).To(Equal(ref.Path))
   301  			Expect(utils.ReadPlainFromOpaque(item.Opaque, "status")).To(Equal("processing"))
   302  
   303  			// bytes are in dedicated path
   304  			_, err = os.Stat(filepath.Join(o.Root, "uploads", uploadID))
   305  			Expect(err).To(BeNil())
   306  
   307  			failPostprocessing(uploadID, events.PPOutcomeAbort)
   308  
   309  			// blobstore still not called now
   310  			bs.AssertNumberOfCalls(GinkgoT(), "Upload", 0)
   311  
   312  			// node gone
   313  			resources, err = fs.ListFolder(ctx, rootRef, []string{}, []string{})
   314  			Expect(err).ToNot(HaveOccurred())
   315  			Expect(len(resources)).To(Equal(0))
   316  
   317  			// bytes are still here
   318  			_, err = os.Stat(filepath.Join(o.Root, "uploads", uploadID))
   319  			Expect(err).To(BeNil())
   320  		})
   321  	})
   322  
   323  	When("the uploaded file creates a new version", func() {
   324  		JustBeforeEach(func() {
   325  			succeedPostprocessing(uploadID)
   326  
   327  			// make sure there is no version yet
   328  			revs, err := fs.ListRevisions(ctx, ref)
   329  			Expect(err).To(BeNil())
   330  			Expect(len(revs)).To(Equal(0))
   331  
   332  			// upload again
   333  			uploadIds, err := fs.InitiateUpload(ctx, ref, 10, map[string]string{})
   334  			Expect(err).ToNot(HaveOccurred())
   335  			Expect(len(uploadIds)).To(Equal(2))
   336  			Expect(uploadIds["simple"]).ToNot(BeEmpty())
   337  			Expect(uploadIds["tus"]).ToNot(BeEmpty())
   338  
   339  			uploadRef := &provider.Reference{Path: "/" + uploadIds["simple"]}
   340  
   341  			_, err = fs.Upload(ctx, storage.UploadRequest{
   342  				Ref:    uploadRef,
   343  				Body:   io.NopCloser(bytes.NewReader(firstContent)),
   344  				Length: int64(len(firstContent)),
   345  			}, nil)
   346  			Expect(err).ToNot(HaveOccurred())
   347  
   348  			uploadID = uploadIds["simple"]
   349  
   350  			// wait for bytes received event
   351  			_, ok := (<-pub).(events.BytesReceived)
   352  			Expect(ok).To(BeTrue())
   353  
   354  			// version already created
   355  			revs, err = fs.ListRevisions(ctx, ref)
   356  			Expect(err).To(BeNil())
   357  			Expect(len(revs)).To(Equal(1))
   358  
   359  			// at this stage: blobstore called once for the original file
   360  			bs.AssertNumberOfCalls(GinkgoT(), "Upload", 1)
   361  
   362  		})
   363  
   364  		It("succeeds eventually, creating a new version", func() {
   365  			succeedPostprocessing(uploadID)
   366  
   367  			// version still existing
   368  			revs, err := fs.ListRevisions(ctx, ref)
   369  			Expect(err).To(BeNil())
   370  			Expect(len(revs)).To(Equal(1))
   371  
   372  			// blobstore now called twice - for original file and new version
   373  			bs.AssertNumberOfCalls(GinkgoT(), "Upload", 2)
   374  
   375  			// bytes are gone from upload path
   376  			_, err = os.Stat(filepath.Join(o.Root, "uploads", uploadID))
   377  			Expect(err).ToNot(BeNil())
   378  		})
   379  
   380  		It("removes new version and restores old one when instructed", func() {
   381  			_, status, _ := fileStatus()
   382  			Expect(status).To(Equal("processing"))
   383  
   384  			failPostprocessing(uploadID, events.PPOutcomeDelete)
   385  
   386  			_, status, _ = fileStatus()
   387  			Expect(status).To(Equal(""))
   388  
   389  			// version gone now
   390  			revs, err := fs.ListRevisions(ctx, ref)
   391  			Expect(err).To(BeNil())
   392  			Expect(len(revs)).To(Equal(0))
   393  
   394  			// bytes are removed from upload path
   395  			_, err = os.Stat(filepath.Join(o.Root, "uploads", uploadID))
   396  			Expect(err).ToNot(BeNil())
   397  
   398  			// blobstore still called only once for the original file
   399  			bs.AssertNumberOfCalls(GinkgoT(), "Upload", 1)
   400  		})
   401  
   402  	})
   403  	When("Two uploads are processed in parallel", func() {
   404  		var secondUploadID string
   405  
   406  		JustBeforeEach(func() {
   407  			// upload again
   408  			uploadIds, err := fs.InitiateUpload(ctx, ref, 20, map[string]string{})
   409  			Expect(err).ToNot(HaveOccurred())
   410  			Expect(len(uploadIds)).To(Equal(2))
   411  			Expect(uploadIds["simple"]).ToNot(BeEmpty())
   412  			Expect(uploadIds["tus"]).ToNot(BeEmpty())
   413  
   414  			uploadRef := &provider.Reference{Path: "/" + uploadIds["simple"]}
   415  
   416  			_, err = fs.Upload(ctx, storage.UploadRequest{
   417  				Ref:    uploadRef,
   418  				Body:   io.NopCloser(bytes.NewReader(secondContent)),
   419  				Length: int64(len(secondContent)),
   420  			}, nil)
   421  			Expect(err).ToNot(HaveOccurred())
   422  
   423  			secondUploadID = uploadIds["simple"]
   424  
   425  			// wait for bytes received event
   426  			_, ok := (<-pub).(events.BytesReceived)
   427  			Expect(ok).To(BeTrue())
   428  		})
   429  
   430  		It("doesn't remove processing status when first upload is finished", func() {
   431  			succeedPostprocessing(uploadID)
   432  
   433  			_, status, _ := fileStatus()
   434  			// check processing status
   435  			Expect(status).To(Equal("processing"))
   436  		})
   437  
   438  		It("removes processing status when second upload is finished, even if first isn't", func() {
   439  			succeedPostprocessing(secondUploadID)
   440  
   441  			_, status, _ := fileStatus()
   442  			Expect(status).To(Equal(""))
   443  		})
   444  
   445  		It("correctly calculates the size when the second upload is finished, even if first is deleted", func() {
   446  			succeedPostprocessing(secondUploadID)
   447  
   448  			_, status, size := fileStatus()
   449  			Expect(status).To(Equal(""))
   450  			// size should match the second upload
   451  			Expect(size).To(Equal(len(secondContent)))
   452  
   453  			// parent size should match second upload as well
   454  			Expect(parentSize()).To(Equal(len(secondContent)))
   455  
   456  			failPostprocessing(uploadID, events.PPOutcomeDelete)
   457  
   458  			// check processing status
   459  			_, _, size = fileStatus()
   460  			// size should still match the second upload
   461  			Expect(size).To(Equal(len(secondContent)))
   462  
   463  			// parent size should still match second upload as well
   464  			Expect(parentSize()).To(Equal(len(secondContent)))
   465  		})
   466  
   467  		It("the first can succeed before the second succeeds", func() {
   468  			succeedPostprocessing(uploadID)
   469  
   470  			_, status, size := fileStatus()
   471  			// check processing status
   472  			Expect(status).To(Equal("processing"))
   473  			// size should match the second upload
   474  			Expect(size).To(Equal((len(secondContent))))
   475  
   476  			// parent size should match the second upload
   477  			Expect(parentSize()).To(Equal(len(secondContent)))
   478  
   479  			succeedPostprocessing(secondUploadID)
   480  
   481  			// check processing status has been removed
   482  			_, status, size = fileStatus()
   483  			Expect(status).To(Equal(""))
   484  
   485  			// size should still match the second upload
   486  			Expect(size).To(Equal(len(secondContent)))
   487  
   488  			// parent size should still match second upload
   489  			Expect(parentSize()).To(Equal(len(secondContent)))
   490  
   491  			// file should have one revision
   492  			Expect(revisionCount()).To(Equal(1))
   493  		})
   494  
   495  		It("the first can succeed after the second succeeds", func() {
   496  			succeedPostprocessing(secondUploadID)
   497  
   498  			_, status, size := fileStatus()
   499  			// check processing status has been removed because the most recent upload finished and can be downloaded
   500  			Expect(status).To(Equal(""))
   501  			// size should match the second upload
   502  			Expect(size).To(Equal(len(secondContent)))
   503  
   504  			// parent size should match second upload as well
   505  			Expect(parentSize()).To(Equal(len(secondContent)))
   506  
   507  			succeedPostprocessing(uploadID)
   508  
   509  			_, status, size = fileStatus()
   510  			// check processing status is still unset
   511  			Expect(status).To(Equal(""))
   512  			// size should still match the second upload
   513  			Expect(size).To(Equal(len(secondContent)))
   514  
   515  			// parent size should still match second upload
   516  			Expect(parentSize()).To(Equal(len(secondContent)))
   517  
   518  			// file should have one revision
   519  			Expect(revisionCount()).To(Equal(1))
   520  		})
   521  
   522  		It("the first can succeed before the second fails", func() {
   523  			succeedPostprocessing(uploadID)
   524  
   525  			_, status, size := fileStatus()
   526  			// check processing status
   527  			Expect(status).To(Equal("processing"))
   528  			// size should match the second upload
   529  			Expect(size).To(Equal(len(secondContent)))
   530  
   531  			// parent size should match the second upload
   532  			Expect(parentSize()).To(Equal(len(secondContent)))
   533  
   534  			failPostprocessing(secondUploadID, events.PPOutcomeDelete)
   535  
   536  			_, status, size = fileStatus()
   537  			// check processing status has been removed
   538  			Expect(status).To(Equal(""))
   539  			// size should match the first upload
   540  			Expect(size).To(Equal(len(firstContent)))
   541  
   542  			// parent size should match first upload
   543  			Expect(parentSize()).To(Equal(len(firstContent)))
   544  
   545  			// file should not have any revisions
   546  			Expect(revisionCount()).To(Equal(0))
   547  		})
   548  
   549  		It("the first can succeed after the second fails", func() {
   550  			failPostprocessing(secondUploadID, events.PPOutcomeDelete)
   551  
   552  			_, _, size := fileStatus()
   553  			// check processing status has not been unset
   554  			// FIXME we need to fall back to the previous processing id
   555  			// Expect(status).To(Equal("processing"))
   556  			// size should match the first upload
   557  			Expect(size).To(Equal(len(firstContent)))
   558  
   559  			// parent size should match first upload as well
   560  			Expect(parentSize()).To(Equal(len(firstContent)))
   561  
   562  			succeedPostprocessing(uploadID)
   563  
   564  			_, status, size := fileStatus()
   565  			// check processing status is now unset
   566  			Expect(status).To(Equal(""))
   567  			// size should still match the first upload
   568  			Expect(size).To(Equal(len(firstContent)))
   569  
   570  			// parent size should still match first upload
   571  			Expect(parentSize()).To(Equal(len(firstContent)))
   572  
   573  			// file should not have any revisions
   574  			Expect(revisionCount()).To(Equal(0))
   575  		})
   576  
   577  		It("the first can fail before the second succeeds", func() {
   578  			failPostprocessing(uploadID, events.PPOutcomeDelete)
   579  
   580  			_, status, size := fileStatus()
   581  			// check processing status
   582  			Expect(status).To(Equal("processing"))
   583  			// size should match the second upload
   584  			Expect(size).To(Equal(len(secondContent)))
   585  
   586  			// parent size should match second upload as well
   587  			Expect(parentSize()).To(Equal(len(secondContent)))
   588  
   589  			succeedPostprocessing(secondUploadID)
   590  
   591  			_, status, size = fileStatus()
   592  			// check processing status has been removed
   593  			Expect(status).To(Equal(""))
   594  			// size should still match the second upload
   595  			Expect(size).To(Equal(len(secondContent)))
   596  
   597  			// parent size should still match second upload
   598  			Expect(parentSize()).To(Equal(len(secondContent)))
   599  
   600  			// file should not have any revisions
   601  			// FIXME we need to delete the revision
   602  			// Expect(revisionCount()).To(Equal(0))
   603  		})
   604  
   605  		It("the first can fail after the second succeeds", func() {
   606  			succeedPostprocessing(secondUploadID)
   607  
   608  			_, status, size := fileStatus()
   609  			// check processing status has been removed because the most recent upload finished and can be downloaded
   610  			Expect(status).To(Equal(""))
   611  			// size should match the second upload
   612  			Expect(size).To(Equal(len(secondContent)))
   613  
   614  			// parent size should match second upload as well
   615  			Expect(parentSize()).To(Equal(len(secondContent)))
   616  
   617  			failPostprocessing(uploadID, events.PPOutcomeDelete)
   618  
   619  			_, status, size = fileStatus()
   620  			// check processing status is still unset
   621  			Expect(status).To(Equal(""))
   622  			// size should still match the second upload
   623  			Expect(size).To(Equal(len(secondContent)))
   624  
   625  			// parent size should still match second upload
   626  			Expect(parentSize()).To(Equal(len(secondContent)))
   627  
   628  			// file should not have any revisions
   629  			// FIXME we need to delete the revision
   630  			// Expect(revisionCount()).To(Equal(0))
   631  		})
   632  
   633  		It("the first can fail before the second fails", func() {
   634  			failPostprocessing(uploadID, events.PPOutcomeDelete)
   635  
   636  			_, status, size := fileStatus()
   637  			// check processing status
   638  			Expect(status).To(Equal("processing"))
   639  			// size should match the second upload
   640  			Expect(size).To(Equal(len(secondContent)))
   641  
   642  			// parent size should match second upload as well
   643  			Expect(parentSize()).To(Equal(len(secondContent)))
   644  
   645  			failPostprocessing(secondUploadID, events.PPOutcomeDelete)
   646  
   647  			// check file has been removed
   648  			// if all uploads have been processed with outcome delete -> delete the file
   649  			// exists, _, _ := fileStatus()
   650  			// FIXME this should be false, but we are not deleting the resource
   651  			// Expect(exists).To(BeFalse())
   652  
   653  			// parent size should be 0
   654  			// FIXME we are not correctly reverting the sizediff
   655  			// Expect(parentSize()).To(Equal(0))
   656  		})
   657  
   658  		It("the first can fail after the second fails", func() {
   659  			failPostprocessing(secondUploadID, events.PPOutcomeDelete)
   660  
   661  			_, status, size := fileStatus()
   662  			// check processing status has been removed because the most recent upload finished and can be downloaded
   663  			Expect(status).To(Equal(""))
   664  			// size should match the first upload
   665  			Expect(size).To(Equal(len(firstContent)))
   666  
   667  			// parent size should match second first as well
   668  			Expect(parentSize()).To(Equal(len(firstContent)))
   669  
   670  			failPostprocessing(uploadID, events.PPOutcomeDelete)
   671  
   672  			// check file has been removed
   673  			// if all uploads have been processed with outcome delete -> delete the file
   674  			// exists, _, _ := fileStatus()
   675  			// FIXME this should be false, but we are not deleting the resource
   676  			// Expect(exists).To(BeFalse())
   677  
   678  			// parent size should be 0
   679  			// FIXME we are not correctly reverting the sizediff
   680  			// Expect(parentSize()).To(Equal(0))
   681  		})
   682  	})
   683  })