tschaub / mock-fs

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

Doesn't seem to work with node 10 fs.promises #245

Closed slikts closed 5 years ago

slikts commented 6 years ago
import  { promises } from 'fs'
mock({
  test: 'file content',
})
await promises.readFile('test', { encoding: 'utf8' })

Produces: EBADF, bad file descriptor

While fs.readFileSync('test', 'utf8') works correctly.

node: 10.6.0 mock-fs: 4.6.0

wibobm commented 6 years ago

The last version of Node this worked on was 10.4.1

SwinX commented 6 years ago

Confirming this may be reproduced on node@10.11 and mock-fs@4.6.0. Any insight about what's causing this issue and when it will be fixed?

shadymoses commented 6 years ago

I'm getting a similar issue. I can successfully read the mock files using fs.readFileSync, but if I convert my operation to be async:

const fs = require('fs').promises
// inside async function
const content = await fs.readFile(file, 'utf8')

it always throws:

Error: ENOENT: no such file or directory, open 'src/client.jsx'

I'm using Node@10.12.0, jest@23.6.0 and mock-fs@4.7.0.

maxwellgerber commented 6 years ago

So a little insight into what's causing the issue:

mock-fs works by overloading Node's process.bindings for the internal C++ FS module. So, you call require('fs').someMethod and that someMethod calls process.bindings('fs').someMethodInternal in turn. After a mock is spun up, the fs module calls into mock-fs instead of the C++ code, allowing us to alter the behavior. Because the lib is tightly coupled to internal APIs, there's no guarantee that it will continue to function on new versions.

[NOTE: This is being phased out entirely in Node 11 due to the deprecation of bindings]

The fs.promises module isn't a wrapper around the original fs module. It does its own thing, and interacts with the underlying process bindings differently. The lib hasn't been updated to handle this new behavior, which is why people get errors when using fs.promises but not with util.promisify(fs.someFunction).

You can see the javascript-land code for fs.promises here.

The biggest change is that there's a symbol passed in to the callback param of the Binding function called kUsePromises. Conceivably, we could check for that symbol inside mock-fs here and respond appropriately. I haven't spent enough time on it to say whether or not some other issues might come up.

Feel free to take a crack at it and open a PR!

shadymoses commented 6 years ago

@maxwellgerber Thanks for the info! If I can find some free time soon I'll look into that.

The weird thing is that I have another test suite that's using fs.promises to walk a directory and extract paths of files matching specific types. It's using readdir and stat from fs.promises and it's passing with mock-fs. It only seems to fail on read or write attempts.

tschaub commented 5 years ago

This should be addressed on the 4.8.0 release (thanks @huochunpeng, see #260).