Skip to content

Commit de743fa

Browse files
committed
feat: ensure destination folder exists before creating write stream in streamFileToDisk
1 parent 9ec3eee commit de743fa

2 files changed

Lines changed: 51 additions & 13 deletions

File tree

Lines changed: 47 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,77 +1,111 @@
11
import fs from 'node:fs';
22
import { PassThrough } from 'node:stream';
3+
import * as ensureFolderExistsModule from '../../../../apps/shared/fs/ensure-folder-exists';
34
import { streamFileToDisk } from './stream-file-to-disk';
4-
import { deepMocked } from '../../../../../tests/vitest/utils.helper';
5+
import { call, calls, partialSpyOn } from '../../../../../tests/vitest/utils.helper';
56

6-
vi.mock(import('node:fs'));
7+
describe('stream-file-to-disk', () => {
8+
const createWriteStreamMock = partialSpyOn(fs, 'createWriteStream');
9+
const ensureFolderExistsMock = partialSpyOn(ensureFolderExistsModule, 'ensureFolderExists');
710

8-
const fsMock = deepMocked(fs);
9-
10-
describe('streamFileToDisk', () => {
1111
let fakeWriteStream: PassThrough & { bytesWritten: number };
1212

1313
beforeEach(() => {
1414
fakeWriteStream = Object.assign(new PassThrough(), { bytesWritten: 0 });
15-
fsMock.createWriteStream.mockReturnValue(fakeWriteStream as any);
15+
createWriteStreamMock.mockReturnValue(fakeWriteStream as unknown as fs.WriteStream);
1616
});
1717

18-
it('should create a write stream at the given file path', () => {
18+
it('should ensure the destination folder exists before creating the stream', () => {
19+
// Given
1920
const readable = new PassThrough();
2021

21-
streamFileToDisk(readable, '/tmp/file', vi.fn());
22+
// When
23+
streamFileToDisk(readable, '/home/dev/.config/internxt/downloaded/file-id', vi.fn());
2224

23-
expect(fsMock.createWriteStream).toHaveBeenCalledWith('/tmp/file');
25+
// Then
26+
call(ensureFolderExistsMock).toBe('/home/dev/.config/internxt/downloaded');
27+
call(createWriteStreamMock).toBe('/home/dev/.config/internxt/downloaded/file-id');
2428
});
2529

2630
it('should pipe the readable into the write stream', () => {
31+
// Given
2732
const readable = new PassThrough();
28-
const pipeSpy = vi.spyOn(readable, 'pipe');
33+
const pipeSpy = partialSpyOn(readable, 'pipe', false);
2934

35+
// When
3036
streamFileToDisk(readable, '/tmp/file', vi.fn());
3137

32-
expect(pipeSpy).toHaveBeenCalledWith(fakeWriteStream);
38+
// Then
39+
call(pipeSpy).toBe(fakeWriteStream);
3340
});
3441

3542
it('should call onProgress with bytesWritten when data is received', () => {
43+
// Given
3644
const readable = new PassThrough();
3745
const onProgress = vi.fn();
3846
fakeWriteStream.bytesWritten = 100;
3947

48+
// When
4049
streamFileToDisk(readable, '/tmp/file', onProgress);
4150
readable.emit('data', Buffer.alloc(100));
4251

43-
expect(onProgress).toHaveBeenCalledWith(100);
52+
// Then
53+
call(onProgress).toBe(100);
4454
});
4555

4656
it('should call onProgress with bytesWritten on drain', () => {
57+
// Given
4758
const readable = new PassThrough();
4859
const onProgress = vi.fn();
4960
fakeWriteStream.bytesWritten = 200;
5061

62+
// When
5163
streamFileToDisk(readable, '/tmp/file', onProgress);
5264
fakeWriteStream.emit('drain');
5365

54-
expect(onProgress).toHaveBeenCalledWith(200);
66+
// Then
67+
call(onProgress).toBe(200);
5568
});
5669

5770
it('should return the write stream', () => {
71+
// Given
5872
const readable = new PassThrough();
5973

74+
// When
6075
const result = streamFileToDisk(readable, '/tmp/file', vi.fn());
6176

77+
// Then
6278
expect(result.writeStream).toBe(fakeWriteStream);
6379
});
6480

6581
it('should return getBytesWritten that tracks progress', () => {
82+
// Given
6683
const readable = new PassThrough();
6784

85+
// When
6886
const result = streamFileToDisk(readable, '/tmp/file', vi.fn());
6987

88+
// Then
7089
expect(result.getBytesWritten()).toBe(0);
7190

91+
// Given
7292
fakeWriteStream.bytesWritten = 500;
93+
94+
// When
7395
readable.emit('data', Buffer.alloc(500));
7496

97+
// Then
7598
expect(result.getBytesWritten()).toBe(500);
7699
});
100+
101+
it('should create the write stream once', () => {
102+
// Given
103+
const readable = new PassThrough();
104+
105+
// When
106+
streamFileToDisk(readable, '/tmp/file', vi.fn());
107+
108+
// Then
109+
calls(createWriteStreamMock).toHaveLength(1);
110+
});
77111
});

src/backend/features/fuse/on-read/stream-file-to-disk.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,13 @@
11
import fs from 'node:fs';
2+
import { dirname } from 'node:path';
23
import { type Readable } from 'node:stream';
4+
import { ensureFolderExists } from '../../../../apps/shared/fs/ensure-folder-exists';
35

46
export function streamFileToDisk(readable: Readable, filePath: string, onProgress: (bytesWritten: number) => void) {
57
let bytesWritten = 0;
68

9+
ensureFolderExists(dirname(filePath));
10+
711
const writeStream = fs.createWriteStream(filePath);
812

913
readable.pipe(writeStream);

0 commit comments

Comments
 (0)