X-Git-Url: https://git.arvados.org/arvados.git/blobdiff_plain/e1cf48a01c6f7aca509eb4aa886bfe530cd18457..3d4f1fd8ce62ed2cd35af23231062a95b70c500b:/src/services/collection-service/collection-service.test.ts diff --git a/src/services/collection-service/collection-service.test.ts b/src/services/collection-service/collection-service.test.ts index 4be17213cd..304cbfd311 100644 --- a/src/services/collection-service/collection-service.test.ts +++ b/src/services/collection-service/collection-service.test.ts @@ -7,7 +7,7 @@ import MockAdapter from 'axios-mock-adapter'; import { snakeCase } from 'lodash'; import { CollectionResource, defaultCollectionSelectedFields } from 'models/collection'; import { AuthService } from '../auth-service/auth-service'; -import { CollectionService } from './collection-service'; +import { CollectionService, emptyCollectionPdh } from './collection-service'; describe('collection-service', () => { let collectionService: CollectionService; @@ -23,10 +23,12 @@ describe('collection-service', () => { webdavClient = { delete: jest.fn(), upload: jest.fn(), + mkdir: jest.fn(), } as any; authService = {} as AuthService; actions = { progressFn: jest.fn(), + errorFn: jest.fn(), } as any; collectionService = new CollectionService(serverApi, webdavClient, authService, actions); collectionService.update = jest.fn(); @@ -127,42 +129,327 @@ describe('collection-service', () => { describe('deleteFiles', () => { it('should remove no files', async () => { // given + serverApi.put = jest.fn(() => Promise.resolve({ data: {} })); const filePaths: string[] = []; - const collectionUUID = ''; + const collectionUUID = 'zzzzz-tpzed-5o5tg0l9a57gxxx'; // when await collectionService.deleteFiles(collectionUUID, filePaths); // then - expect(webdavClient.delete).not.toHaveBeenCalled(); + expect(serverApi.put).toHaveBeenCalledTimes(1); + expect(serverApi.put).toHaveBeenCalledWith( + `/collections/${collectionUUID}`, { + collection: { + preserve_version: true + }, + replace_files: {}, + } + ); }); it('should remove only root files', async () => { // given + serverApi.put = jest.fn(() => Promise.resolve({ data: {} })); const filePaths: string[] = ['/root/1', '/root/1/100', '/root/1/100/test.txt', '/root/2', '/root/2/200', '/root/3/300/test.txt']; - const collectionUUID = ''; + const collectionUUID = 'zzzzz-tpzed-5o5tg0l9a57gxxx'; // when await collectionService.deleteFiles(collectionUUID, filePaths); // then - expect(webdavClient.delete).toHaveBeenCalledTimes(3); - expect(webdavClient.delete).toHaveBeenCalledWith("c=/root/3/300/test.txt"); - expect(webdavClient.delete).toHaveBeenCalledWith("c=/root/2"); - expect(webdavClient.delete).toHaveBeenCalledWith("c=/root/1"); + expect(serverApi.put).toHaveBeenCalledTimes(1); + expect(serverApi.put).toHaveBeenCalledWith( + `/collections/${collectionUUID}`, { + collection: { + preserve_version: true + }, + replace_files: { + '/root/3/300/test.txt': '', + '/root/2': '', + '/root/1': '', + }, + } + ); }); - it('should remove files with uuid prefix', async () => { + it('should batch remove files', async () => { + serverApi.put = jest.fn(() => Promise.resolve({ data: {} })); // given - const filePaths: string[] = ['/root/1']; - const collectionUUID = 'zzzzz-tpzed-5o5tg0l9a57gxxx'; + const filePaths: string[] = ['/root/1', '/secondFile', 'barefile.txt']; + const collectionUUID = 'zzzzz-4zz18-5o5tg0l9a57gxxx'; // when await collectionService.deleteFiles(collectionUUID, filePaths); // then - expect(webdavClient.delete).toHaveBeenCalledTimes(1); - expect(webdavClient.delete).toHaveBeenCalledWith("c=zzzzz-tpzed-5o5tg0l9a57gxxx/root/1"); + expect(serverApi.put).toHaveBeenCalledTimes(1); + expect(serverApi.put).toHaveBeenCalledWith( + `/collections/${collectionUUID}`, { + collection: { + preserve_version: true + }, + replace_files: { + '/root/1': '', + '/secondFile': '', + '/barefile.txt': '', + }, + } + ); + }); + }); + + describe('renameFile', () => { + it('should rename file', async () => { + serverApi.put = jest.fn(() => Promise.resolve({ data: {} })); + const collectionUuid = 'zzzzz-4zz18-ywq0rvhwwhkjnfq'; + const collectionPdh = '8cd9ce1dfa21c635b620b1bfee7aaa08+180'; + const oldPath = '/old/path'; + const newPath = '/new/filename'; + + await collectionService.renameFile(collectionUuid, collectionPdh, oldPath, newPath); + + expect(serverApi.put).toHaveBeenCalledTimes(1); + expect(serverApi.put).toHaveBeenCalledWith( + `/collections/${collectionUuid}`, { + collection: { + preserve_version: true + }, + replace_files: { + [newPath]: `${collectionPdh}${oldPath}`, + [oldPath]: '', + }, + } + ); }); }); + + describe('copyFiles', () => { + it('should batch copy files', async () => { + serverApi.put = jest.fn(() => Promise.resolve({ data: {} })); + const filePaths: string[] = ['/root/1', '/secondFile', 'barefile.txt']; + const sourcePdh = '8cd9ce1dfa21c635b620b1bfee7aaa08+180'; + + const destinationUuid = 'zzzzz-4zz18-ywq0rvhwwhkjnfq'; + const destinationPath = '/destinationPath'; + + // when + await collectionService.copyFiles(sourcePdh, filePaths, destinationUuid, destinationPath); + + // then + expect(serverApi.put).toHaveBeenCalledTimes(1); + expect(serverApi.put).toHaveBeenCalledWith( + `/collections/${destinationUuid}`, { + collection: { + preserve_version: true + }, + replace_files: { + [`${destinationPath}/1`]: `${sourcePdh}/root/1`, + [`${destinationPath}/secondFile`]: `${sourcePdh}/secondFile`, + [`${destinationPath}/barefile.txt`]: `${sourcePdh}/barefile.txt`, + }, + } + ); + }); + + it('should copy files from rooth', async () => { + // Test copying from root paths + serverApi.put = jest.fn(() => Promise.resolve({ data: {} })); + const filePaths: string[] = ['/']; + const sourcePdh = '8cd9ce1dfa21c635b620b1bfee7aaa08+180'; + + const destinationUuid = 'zzzzz-4zz18-ywq0rvhwwhkjnfq'; + const destinationPath = '/destinationPath'; + + await collectionService.copyFiles(sourcePdh, filePaths, destinationUuid, destinationPath); + + expect(serverApi.put).toHaveBeenCalledTimes(1); + expect(serverApi.put).toHaveBeenCalledWith( + `/collections/${destinationUuid}`, { + collection: { + preserve_version: true + }, + replace_files: { + [`${destinationPath}`]: `${sourcePdh}/`, + }, + } + ); + }); + + it('should copy files to root path', async () => { + // Test copying to root paths + serverApi.put = jest.fn(() => Promise.resolve({ data: {} })); + const filePaths: string[] = ['/']; + const sourcePdh = '8cd9ce1dfa21c635b620b1bfee7aaa08+180'; + + const destinationUuid = 'zzzzz-4zz18-ywq0rvhwwhkjnfq'; + const destinationPath = '/'; + + await collectionService.copyFiles(sourcePdh, filePaths, destinationUuid, destinationPath); + + expect(serverApi.put).toHaveBeenCalledTimes(1); + expect(serverApi.put).toHaveBeenCalledWith( + `/collections/${destinationUuid}`, { + collection: { + preserve_version: true + }, + replace_files: { + "/": `${sourcePdh}/`, + }, + } + ); + }); + }); + + describe('moveFiles', () => { + it('should batch move files', async () => { + serverApi.put = jest.fn(() => Promise.resolve({ data: {} })); + // given + const filePaths: string[] = ['/rootFile', '/secondFile', '/subpath/subfile', 'barefile.txt']; + const srcCollectionUUID = 'zzzzz-4zz18-5o5tg0l9a57gxxx'; + const srcCollectionPdh = '8cd9ce1dfa21c635b620b1bfee7aaa08+180'; + + const destinationUuid = 'zzzzz-4zz18-ywq0rvhwwhkjnfq'; + const destinationPath = '/destinationPath'; + + // when + await collectionService.moveFiles(srcCollectionUUID, srcCollectionPdh, filePaths, destinationUuid, destinationPath); + + // then + expect(serverApi.put).toHaveBeenCalledTimes(2); + // Verify copy + expect(serverApi.put).toHaveBeenCalledWith( + `/collections/${destinationUuid}`, { + collection: { + preserve_version: true + }, + replace_files: { + [`${destinationPath}/rootFile`]: `${srcCollectionPdh}/rootFile`, + [`${destinationPath}/secondFile`]: `${srcCollectionPdh}/secondFile`, + [`${destinationPath}/subfile`]: `${srcCollectionPdh}/subpath/subfile`, + [`${destinationPath}/barefile.txt`]: `${srcCollectionPdh}/barefile.txt`, + }, + } + ); + // Verify delete + expect(serverApi.put).toHaveBeenCalledWith( + `/collections/${srcCollectionUUID}`, { + collection: { + preserve_version: true + }, + replace_files: { + "/rootFile": "", + "/secondFile": "", + "/subpath/subfile": "", + "/barefile.txt": "", + }, + } + ); + }); + + it('should batch move files within collection', async () => { + serverApi.put = jest.fn(() => Promise.resolve({ data: {} })); + // given + const filePaths: string[] = ['/one', '/two', '/subpath/subfile', 'barefile.txt']; + const srcCollectionUUID = 'zzzzz-4zz18-5o5tg0l9a57gxxx'; + const srcCollectionPdh = '8cd9ce1dfa21c635b620b1bfee7aaa08+180'; + + const destinationPath = '/destinationPath'; + + // when + await collectionService.moveFiles(srcCollectionUUID, srcCollectionPdh, filePaths, srcCollectionUUID, destinationPath); + + // then + expect(serverApi.put).toHaveBeenCalledTimes(1); + // Verify copy + expect(serverApi.put).toHaveBeenCalledWith( + `/collections/${srcCollectionUUID}`, { + collection: { + preserve_version: true + }, + replace_files: { + [`${destinationPath}/one`]: `${srcCollectionPdh}/one`, + ['/one']: '', + [`${destinationPath}/two`]: `${srcCollectionPdh}/two`, + ['/two']: '', + [`${destinationPath}/subfile`]: `${srcCollectionPdh}/subpath/subfile`, + ['/subpath/subfile']: '', + [`${destinationPath}/barefile.txt`]: `${srcCollectionPdh}/barefile.txt`, + ['/barefile.txt']: '', + }, + } + ); + }); + + it('should abort batch move when copy fails', async () => { + // Simulate failure to copy + serverApi.put = jest.fn(() => Promise.reject({ + data: {}, + response: { + "errors": ["error getting snapshot of \"rootFile\" from \"8cd9ce1dfa21c635b620b1bfee7aaa08+180\": file does not exist"] + } + })); + // given + const filePaths: string[] = ['/rootFile', '/secondFile', '/subpath/subfile', 'barefile.txt']; + const srcCollectionUUID = 'zzzzz-4zz18-5o5tg0l9a57gxxx'; + const srcCollectionPdh = '8cd9ce1dfa21c635b620b1bfee7aaa08+180'; + + const destinationUuid = 'zzzzz-4zz18-ywq0rvhwwhkjnfq'; + const destinationPath = '/destinationPath'; + + // when + try { + await collectionService.moveFiles(srcCollectionUUID, srcCollectionPdh, filePaths, destinationUuid, destinationPath); + } catch {} + + // then + expect(serverApi.put).toHaveBeenCalledTimes(1); + // Verify copy + expect(serverApi.put).toHaveBeenCalledWith( + `/collections/${destinationUuid}`, { + collection: { + preserve_version: true + }, + replace_files: { + [`${destinationPath}/rootFile`]: `${srcCollectionPdh}/rootFile`, + [`${destinationPath}/secondFile`]: `${srcCollectionPdh}/secondFile`, + [`${destinationPath}/subfile`]: `${srcCollectionPdh}/subpath/subfile`, + [`${destinationPath}/barefile.txt`]: `${srcCollectionPdh}/barefile.txt`, + }, + } + ); + }); + }); + + describe('createDirectory', () => { + it('creates empty directory', async () => { + // given + const directoryNames = [ + {in: 'newDir', out: 'newDir'}, + {in: '/fooDir', out: 'fooDir'}, + {in: '/anotherPath/', out: 'anotherPath'}, + {in: 'trailingSlash/', out: 'trailingSlash'}, + ]; + const collectionUuid = 'zzzzz-tpzed-5o5tg0l9a57gxxx'; + + for (var i = 0; i < directoryNames.length; i++) { + serverApi.put = jest.fn(() => Promise.resolve({ data: {} })); + // when + await collectionService.createDirectory(collectionUuid, directoryNames[i].in); + // then + expect(serverApi.put).toHaveBeenCalledTimes(1); + expect(serverApi.put).toHaveBeenCalledWith( + `/collections/${collectionUuid}`, { + collection: { + preserve_version: true + }, + replace_files: { + ["/" + directoryNames[i].out]: emptyCollectionPdh, + }, + } + ); + } + }); + }); + });