-
Notifications
You must be signed in to change notification settings - Fork 26
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #1089 from internxt/bugfix/PB-1948-downloading-zip…
…-error [PB-1948] bugfix/Downloading zip error
- Loading branch information
Showing
6 changed files
with
428 additions
and
137 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,3 @@ | ||
export interface Iterator<T> { | ||
next(): Promise<{ value: T[]; done: boolean }>; | ||
next(): Promise<{ value: T[]; done: boolean; token?: string }>; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,185 @@ | ||
import { FlatFolderZip } from '../../core/services/zip.service'; | ||
import { addAllFilesToZip, addAllSharedFilesToZip } from './filesZip.service'; | ||
|
||
const mockDownloadFile = jest.fn(); | ||
|
||
class MockFlatFolderZip { | ||
// zip variable public to spy with Jest | ||
public zip: any; | ||
private passThrough: any; | ||
private folderName: string; | ||
|
||
constructor(folderName: string) { | ||
this.folderName = folderName; | ||
this.zip = { | ||
addFile: jest.fn(), | ||
addFolder: jest.fn(), | ||
end: jest.fn(), | ||
}; | ||
this.passThrough = { | ||
pipeTo: jest.fn().mockReturnValue(Promise.resolve()), | ||
}; | ||
} | ||
|
||
addFile(name: string, source: ReadableStream<Uint8Array>): void { | ||
this.zip.addFile(name, source); | ||
} | ||
|
||
addFolder(name: string): void { | ||
this.zip.addFolder(name); | ||
} | ||
|
||
async close(): Promise<void> { | ||
await this.zip.end(); | ||
} | ||
} | ||
|
||
describe('filesZip', () => { | ||
const filesPage1 = [ | ||
{ name: 'file1', type: 'txt' }, | ||
{ name: 'file2', type: 'pdf' }, | ||
{ name: 'file4', type: 'pdf' }, | ||
{ name: 'file44', type: 'txt' }, | ||
]; | ||
|
||
const filesPage2 = [ | ||
{ name: 'file1', type: 'txt' }, | ||
{ name: 'file2', type: 'pdf' }, | ||
{ name: 'file4', type: 'pdf' }, | ||
{ name: 'file44', type: 'txt' }, | ||
]; | ||
|
||
const filesPage3 = [ | ||
{ name: 'file1', type: 'txt' }, | ||
{ name: 'file2', type: 'pdf' }, | ||
{ name: 'file4', type: 'pdf' }, | ||
{ name: 'file44', type: 'txt' }, | ||
]; | ||
|
||
let iterator = { | ||
next: jest | ||
.fn() | ||
.mockReturnValueOnce({ value: filesPage1, done: false }) | ||
.mockReturnValueOnce({ value: filesPage2, done: false }) | ||
.mockReturnValueOnce({ value: filesPage3, done: true }), | ||
}; | ||
|
||
let sharedIterator = { | ||
next: jest | ||
.fn() | ||
.mockReturnValueOnce({ value: filesPage1, done: false, token: 'token' }) | ||
.mockReturnValueOnce({ value: filesPage2, done: false, token: 'token' }) | ||
.mockReturnValueOnce({ value: filesPage3, done: true, token: 'token' }), | ||
}; | ||
|
||
const zip = new MockFlatFolderZip('folderName') as unknown as MockFlatFolderZip; | ||
|
||
afterEach(() => { | ||
jest.clearAllMocks(); | ||
iterator = { | ||
next: jest | ||
.fn() | ||
.mockReturnValueOnce({ value: filesPage1, done: false }) | ||
.mockReturnValueOnce({ value: filesPage2, done: false }) | ||
.mockReturnValueOnce({ value: filesPage3, done: true }), | ||
}; | ||
sharedIterator = { | ||
next: jest | ||
.fn() | ||
.mockReturnValueOnce({ value: filesPage1, done: false, token: 'token' }) | ||
.mockReturnValueOnce({ value: filesPage2, done: false, token: 'token' }) | ||
.mockReturnValueOnce({ value: filesPage3, done: true, token: 'token' }), | ||
}; | ||
}); | ||
describe('addAllFilesToZip', () => { | ||
test('should add all files to the zip correctly', async () => { | ||
mockDownloadFile.mockResolvedValue('Mocked file stream'); | ||
const zip = new MockFlatFolderZip('folderName'); | ||
|
||
const result = await addAllFilesToZip( | ||
'/path/to/files', | ||
mockDownloadFile, | ||
iterator, | ||
zip as unknown as FlatFolderZip, | ||
); | ||
const addFile = jest.spyOn(zip.zip, 'addFile'); | ||
|
||
const allFilesLength = filesPage1.length + filesPage2.length + filesPage3.length; | ||
const allFiles = [...filesPage1, ...filesPage2, ...filesPage3]; | ||
expect(mockDownloadFile).toHaveBeenCalledTimes(allFilesLength); | ||
expect(addFile).toHaveBeenCalledTimes(allFilesLength); | ||
expect(result).toEqual(allFiles); | ||
}); | ||
|
||
test('should handle empty iterator correctly', async () => { | ||
const result = await addAllFilesToZip( | ||
'/path/to/files', | ||
mockDownloadFile, | ||
{ next: jest.fn().mockReturnValue({ value: [], done: true }) }, | ||
zip as unknown as FlatFolderZip, | ||
); | ||
const addFile = jest.spyOn(zip.zip, 'addFile'); | ||
|
||
expect(mockDownloadFile).not.toHaveBeenCalled(); | ||
expect(addFile).not.toHaveBeenCalled(); | ||
expect(result).toEqual([]); | ||
}); | ||
|
||
test('should handle errors during file download', async () => { | ||
mockDownloadFile.mockRejectedValueOnce(new Error('Download error')); | ||
const addFile = jest.spyOn(zip.zip, 'addFile'); | ||
|
||
await expect( | ||
addAllFilesToZip('/path/to/files', mockDownloadFile, iterator, zip as unknown as FlatFolderZip), | ||
).rejects.toThrow('Download error'); | ||
|
||
expect(addFile).not.toHaveBeenCalled(); | ||
}); | ||
}); | ||
describe('addAllSharedFilesToZip', () => { | ||
test('should add all shared files to the zip correctly', async () => { | ||
mockDownloadFile.mockResolvedValue('Mocked file stream'); | ||
const zip = new MockFlatFolderZip('folderName'); | ||
|
||
const result = await addAllSharedFilesToZip( | ||
'/path/to/files', | ||
mockDownloadFile, | ||
sharedIterator, | ||
zip as unknown as FlatFolderZip, | ||
); | ||
const addFile = jest.spyOn(zip.zip, 'addFile'); | ||
const allFilesLength = filesPage1.length + filesPage2.length + filesPage3.length; | ||
const allFiles = [...filesPage1, ...filesPage2, ...filesPage3]; | ||
expect(mockDownloadFile).toHaveBeenCalledTimes(allFilesLength); | ||
expect(addFile).toHaveBeenCalledTimes(allFilesLength); | ||
expect(result.files).toEqual(allFiles); | ||
expect(result.token).toEqual('token'); | ||
}); | ||
|
||
test('should handle empty shared iterator correctly', async () => { | ||
const result = await addAllSharedFilesToZip( | ||
'/path/to/files', | ||
mockDownloadFile, | ||
{ next: jest.fn().mockReturnValue({ value: [], done: true, token: 'token' }) }, | ||
zip as unknown as FlatFolderZip, | ||
); | ||
const addFile = jest.spyOn(zip.zip, 'addFile'); | ||
|
||
expect(mockDownloadFile).not.toHaveBeenCalled(); | ||
expect(addFile).not.toHaveBeenCalled(); | ||
expect(result.files).toEqual([]); | ||
expect(result.token).toEqual('token'); | ||
}); | ||
|
||
test('should handle errors during shared file download', async () => { | ||
mockDownloadFile.mockRejectedValueOnce(new Error('Download error')); | ||
const addFile = jest.spyOn(zip.zip, 'addFile'); | ||
|
||
await expect( | ||
addAllSharedFilesToZip('/path/to/files', mockDownloadFile, sharedIterator, zip as unknown as FlatFolderZip), | ||
).rejects.toThrow('Download error'); | ||
|
||
expect(addFile).not.toHaveBeenCalled(); | ||
}); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
import { SharedFiles } from '@internxt/sdk/dist/drive/share/types'; | ||
import { Iterator } from 'app/core/collections'; | ||
import { FlatFolderZip } from '../../core/services/zip.service'; | ||
import { DriveFileData } from '../types'; | ||
|
||
type File = SharedFiles | DriveFileData; | ||
|
||
async function addFilesToZip<T extends File>( | ||
currentAbsolutePath: string, | ||
downloadFile: (file: T) => Promise<ReadableStream>, | ||
iterator: Iterator<T>, | ||
zip: FlatFolderZip, | ||
): Promise<{ files: T[]; token?: string }> { | ||
const path = currentAbsolutePath; | ||
const allFiles: T[] = []; | ||
|
||
const addFileToZip = async (file: T) => { | ||
const fileStream = await downloadFile(file); | ||
zip.addFile(path + '/' + file.name + (file.type ? '.' + file.type : ''), fileStream); | ||
}; | ||
|
||
let pack; | ||
let moreFiles = true; | ||
while (moreFiles) { | ||
pack = await iterator.next(); | ||
|
||
const files = pack.value; | ||
moreFiles = !pack.done; | ||
allFiles.push(...files); | ||
|
||
for (const file of files) { | ||
await addFileToZip(file); | ||
} | ||
} | ||
return { files: allFiles, token: pack?.token ?? '' }; | ||
} | ||
|
||
async function addAllFilesToZip( | ||
currentAbsolutePath: string, | ||
downloadFile: (file: DriveFileData) => Promise<ReadableStream>, | ||
iterator: Iterator<DriveFileData>, | ||
zip: FlatFolderZip, | ||
): Promise<DriveFileData[]> { | ||
const { files } = await addFilesToZip<DriveFileData>(currentAbsolutePath, downloadFile, iterator, zip); | ||
return files; | ||
} | ||
|
||
async function addAllSharedFilesToZip( | ||
currentAbsolutePath: string, | ||
downloadFile: (file: SharedFiles) => Promise<ReadableStream>, | ||
iterator: Iterator<SharedFiles>, | ||
zip: FlatFolderZip, | ||
): Promise<{ files: SharedFiles[]; token: string }> { | ||
const { files, token } = await addFilesToZip<SharedFiles>(currentAbsolutePath, downloadFile, iterator, zip); | ||
return { files, token: token ?? '' }; | ||
} | ||
|
||
export { addAllFilesToZip, addAllSharedFilesToZip }; |
Oops, something went wrong.