forked from internxt/drive-desktop
-
Notifications
You must be signed in to change notification settings - Fork 4
feat: implement download progress tracking in StorageFileDownloader and related components #234
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
8 commits
Select commit
Hold shift + click to select a range
168c4c9
feat: implement download progress tracking in StorageFileDownloader a…
egalvis27 4d92b99
feat: enable antivirus IPC initialization on user login and check ant…
egalvis27 4364599
refactor: simplify downloadFinished method signature in DownloadProgr…
egalvis27 b9adf77
test: update downloadFinished notification to exclude elapsed time
egalvis27 a0cb7a4
feat: refactor download progress tracking and notification system
egalvis27 86f3a16
feat: implement download progress tracking in downloadWithProgressTra…
egalvis27 0383ff2
refactor: clean up formatting in downloadWithProgressTracking and Sto…
egalvis27 416c507
test: add unit tests for readStreamToBuffer and writeReadableToFile f…
egalvis27 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or 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
This file was deleted.
Oops, something went wrong.
This file contains hidden or 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,48 @@ | ||
| import { Readable } from 'stream'; | ||
| import { readStreamToBuffer } from './read-stream-to-buffer'; | ||
| import { calls } from 'tests/vitest/utils.helper'; | ||
|
|
||
| describe('readStreamToBuffer', () => { | ||
| it('returns the full buffer and reports progress', async () => { | ||
| const onProgress = vi.fn(); | ||
| const stream = Readable.from([Buffer.from('he'), Buffer.from('llo')]); | ||
|
|
||
| const result = await readStreamToBuffer({ stream, onProgress }); | ||
|
|
||
| expect(result).toEqual(Buffer.from('hello')); | ||
| calls(onProgress).toHaveLength(2); | ||
| calls(onProgress).toMatchObject([2, 5]); | ||
| }); | ||
|
|
||
| it('returns an empty buffer and does not call onProgress when stream is empty', async () => { | ||
| const onProgress = vi.fn(); | ||
| const stream = Readable.from([]); | ||
|
|
||
| const result = await readStreamToBuffer({ stream, onProgress }); | ||
|
|
||
| expect(result).toEqual(Buffer.alloc(0)); | ||
| calls(onProgress).toHaveLength(0); | ||
| }); | ||
|
|
||
| it('handles a single chunk correctly', async () => { | ||
| const onProgress = vi.fn(); | ||
| const stream = Readable.from([Buffer.from('world')]); | ||
|
|
||
| const result = await readStreamToBuffer({ stream, onProgress }); | ||
|
|
||
| expect(result).toEqual(Buffer.from('world')); | ||
| calls(onProgress).toHaveLength(1); | ||
| calls(onProgress).toMatchObject([5]); | ||
| }); | ||
|
|
||
| it('rejects when the stream emits an error', async () => { | ||
| const onProgress = vi.fn(); | ||
| const stream = new Readable({ | ||
| read() { | ||
| this.destroy(new Error('stream failure')); | ||
| }, | ||
| }); | ||
|
|
||
| await expect(readStreamToBuffer({ stream, onProgress })).rejects.toThrow('stream failure'); | ||
| }); | ||
| }); |
This file contains hidden or 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,28 @@ | ||
| import { Readable, Writable, pipeline } from 'stream'; | ||
| import { promisify } from 'util'; | ||
|
|
||
| const promisifiedPipeline = promisify(pipeline); | ||
|
|
||
| type Props = { | ||
| stream: Readable; | ||
| onProgress: (bytesWritten: number) => void; | ||
| }; | ||
|
|
||
| export async function readStreamToBuffer({ stream, onProgress }: Props) { | ||
| const bufferArray: any[] = []; | ||
| let bytesWritten = 0; | ||
|
|
||
| const bufferWriter = new Writable({ | ||
| write: (chunk, _, callback) => { | ||
| bufferArray.push(chunk); | ||
| bytesWritten += chunk.length; | ||
| onProgress(bytesWritten); | ||
|
|
||
| callback(); | ||
| }, | ||
| }); | ||
|
|
||
| await promisifiedPipeline(stream, bufferWriter); | ||
|
|
||
| return Buffer.concat(bufferArray); | ||
| } | ||
This file contains hidden or 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,52 @@ | ||
| import fs from 'fs'; | ||
| import { Readable, PassThrough } from 'stream'; | ||
| import { writeReadableToFile } from './write-readable-to-file'; | ||
| import { calls } from 'tests/vitest/utils.helper'; | ||
|
|
||
| vi.mock('fs'); | ||
|
|
||
| const mockedFS = vi.mocked(fs, true); | ||
|
|
||
| describe('writeReadableToFile', () => { | ||
| it('writes the readable to the given path and reports progress', async () => { | ||
| const onProgress = vi.fn(); | ||
| const writable = new PassThrough(); | ||
| mockedFS.createWriteStream.mockReturnValue(writable as unknown as fs.WriteStream); | ||
|
|
||
| const readable = Readable.from([Buffer.from('he'), Buffer.from('llo')]); | ||
|
|
||
| const promise = writeReadableToFile({ | ||
| readable, | ||
| path: '/tmp/test-file.txt', | ||
| onProgress, | ||
| }); | ||
|
|
||
| await promise; | ||
|
|
||
| expect(mockedFS.createWriteStream).toHaveBeenCalledWith('/tmp/test-file.txt'); | ||
| calls(onProgress).toHaveLength(2); | ||
| calls(onProgress).toMatchObject([2, 5]); | ||
| }); | ||
|
|
||
| it('rejects when the writable stream emits an error', async () => { | ||
| const onProgress = vi.fn(); | ||
| const writable = new PassThrough(); | ||
| mockedFS.createWriteStream.mockReturnValue(writable as unknown as fs.WriteStream); | ||
|
|
||
| const readable = new Readable({ | ||
| read() {}, | ||
| }); | ||
|
|
||
| const promise = writeReadableToFile({ | ||
| readable, | ||
| path: '/tmp/fail.txt', | ||
| onProgress, | ||
| }); | ||
|
|
||
| const error = new Error('disk full'); | ||
| writable.destroy(error); | ||
|
|
||
| await expect(promise).rejects.toThrow('disk full'); | ||
| calls(onProgress).toHaveLength(0); | ||
| }); | ||
| }); |
This file contains hidden or 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,15 +1,26 @@ | ||
| import fs, { PathLike } from 'fs'; | ||
| import { Readable } from 'stream'; | ||
|
|
||
| export class WriteReadableToFile { | ||
| static write(readable: Readable, path: PathLike): Promise<void> { | ||
| const writableStream = fs.createWriteStream(path); | ||
| type Props = { | ||
| readable: Readable; | ||
| path: PathLike; | ||
| onProgress: (bytesWritten: number) => void; | ||
| }; | ||
|
|
||
| readable.pipe(writableStream); | ||
| export function writeReadableToFile({ readable, path, onProgress }: Props) { | ||
| const writableStream = fs.createWriteStream(path); | ||
|
|
||
| return new Promise<void>((resolve, reject) => { | ||
| writableStream.on('finish', resolve); | ||
| writableStream.on('error', reject); | ||
| }); | ||
| } | ||
| let bytesWritten = 0; | ||
|
|
||
| readable.on('data', (chunk: Buffer) => { | ||
| bytesWritten += chunk.length; | ||
| onProgress(bytesWritten); | ||
| }); | ||
|
|
||
| readable.pipe(writableStream); | ||
|
|
||
| return new Promise<void>((resolve, reject) => { | ||
| writableStream.on('finish', resolve); | ||
| writableStream.on('error', reject); | ||
| }); | ||
| } |
This file contains hidden or 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
This file contains hidden or 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
28 changes: 4 additions & 24 deletions
28
src/context/storage/StorageFiles/__mocks__/DownloadProgressTrackerMock.ts
This file contains hidden or 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,28 +1,8 @@ | ||
| import { DownloadProgressTracker } from '../../../shared/domain/DownloadProgressTracker'; | ||
|
|
||
| export class DownloadProgressTrackerMock implements DownloadProgressTracker { | ||
| private downloadStartedMock = vi.fn(); | ||
| private downloadUpdateMock = vi.fn(); | ||
| private downloadFinishedMock = vi.fn(); | ||
| private errorMock = vi.fn(); | ||
|
|
||
| downloadStarted(name: string, extension: string, size: number): Promise<void> { | ||
| return this.downloadStartedMock(name, extension, size); | ||
| } | ||
|
|
||
| downloadUpdate( | ||
| name: string, | ||
| extension: string, | ||
| progress: { elapsedTime: number; percentage: number }, | ||
| ): Promise<void> { | ||
| return this.downloadUpdateMock(name, extension, progress); | ||
| } | ||
|
|
||
| downloadFinished(name: string, extension: string, size: number, progress: { elapsedTime: number }): Promise<void> { | ||
| return this.downloadFinishedMock(name, extension, size, progress); | ||
| } | ||
|
|
||
| error(name: string, extension: string): Promise<void> { | ||
| return this.errorMock(name, extension); | ||
| } | ||
| downloadStarted = vi.fn(); | ||
| downloadUpdate = vi.fn(); | ||
| downloadFinished = vi.fn(); | ||
| error = vi.fn(); | ||
| } |
This file contains hidden or 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
This file contains hidden or 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
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You could add tests here