tschaub / mock-fs

Configurable mock for the fs module
https://npmjs.org/package/mock-fs
Other
906 stars 86 forks source link

Unexpected behavior when using read and write streams #339

Closed D10f closed 2 years ago

D10f commented 2 years ago

Hi, I'm running into a couple of strange errors while working with both create and write streams. I have two separate functions, one for each type of stream, and I'm testing them as follows:

import fs from 'fs';
import { promisify } from 'util';
import mock from 'mock-fs';

const fileStats = promisify(fs.stat);

beforeEach(() => {
  mock({
    '/tmp': {},
    '/tmp/deleteTestDir': {},
    '/tmp/writeTestDir': {},
    '/tmp/testDirectory': {
      'file1': 'hello',
      'file2': 'again',
      'file3': 'world',
    }
  });
});

afterEach(() => {
  mock.restore();
});

test('Should return a stream with file contents', () => {
  const stream = FileService.readAsStream('/tmp/testDirectory/file1');
  expect(stream instanceof fs.ReadStream).toBe(true);
});

test('Should write data to file', (done) => {
  const fileObj = {
    location: '/tmp/writeTestDir',
    data: Buffer.from('Oh la la!'),
    contentParts: 1,
    currentChunk: '1'
  };

  FileService.writeFile(fileObj)
    .catch(err => console.log(err.message));

  setTimeout(() => {
    fileStats('/tmp/writeTestDir/1')
      .then(exists => {
        expect(exists).toBeTruthy();
        done();
      })
  }, 1000);
});

Separately, they work fine and as expected, and by that I mean when I use either test.only or test.skip so they never run at the same time. However when I attempt to run the entire suite, or at least these two functions together, the second test (write stream) fails:

Error [ERR_UNHANDLED_ERROR]: Unhandled error. (Error {
  message: 'EBADF, bad file descriptor',
  code: 'EBADF',
  errno: -9
})

I have tried adding the --runInBand flag to jest but this made no difference. Note that the streams are working with different files (I tried using different directories as well) and when I comment out the mock-fs import and setup, that is, using the real file system, everything works just fine and as intended.

In case you need it, the real implementation of the functions is below (nothing is mocked, only the file system). My system is Ubuntu 20.04 and node version 14.18.1

readAsStream(filepath: fs.PathLike): fs.ReadStream {
  return fs.createReadStream(filepath);
},

writeFile(file : writeFileInterface): Promise<boolean> {

  const { location, data, contentParts, currentChunk } = file;

  return new Promise((resolve, reject) => {
    const writer = fs.createWriteStream(path.join(location, currentChunk));
    writer.write(data);
    writer.on('error', (err) => reject(err.message));
    writer.on('finish', async () => {
      const files = await this.readDirectory(location);

      if (files.length !== contentParts) {
        resolve(false);
      } else {
        resolve(true); // entire file has been uploaded
      }
    });
  });
},

Thank you!

D10f commented 2 years ago

Quick update, it seems the problem is caused by fs.createReadStream as it affects the test running immediately after it. I was able to confirm this by simply moving the tests around and it did allow the writeStream to work normally, as explained above, but caused another test to fail which also relied on the mocked fs (no streams involved however)

3cp commented 2 years ago

Can you try mock-fs v4.14.0? This issue might be related to our new way of patching readstream for Nodejs v16. As you are still using nodejs v14, mock-fs v4.14.0 might work better for you.

D10f commented 2 years ago

Hi @3cp, sorry for my late response.

I have just now tried to use mock-fs v4.14.0 but I still have the same issue. However, on a Docker container running node:16-buster all the tests passed as expected, with both mock-fs v4.14.0 and mock-fs v5.1.2 so it's most likely an issue only while running a lower version of Node.

As I will be upgrading soon anyway, I think this will do it for me so I'll close this issue. However I will report back if I run into more errors when I test this for real in my machine.

Thanks for the help!