current commit
261025.7 · vfs: fix readlink, realpath and mkdtemp encoding
// Flags: --disable-warning=ExperimentalWarning
'use strict';

// readlink(), realpath() and mkdtemp() on a mounted VFS apply the encoding
// option the way fs does: given as a string or in an object, 'buffer' or any
// other encoding, and a Buffer mkdtemp() prefix always gives a Buffer. The
// VirtualFileSystem readlink and realpath methods do the same.

const common = require('../common');
const assert = require('assert');
const fs = require('fs');
const path = require('path');
const { promisify } = require('util');
const vfs = require('node:vfs');
const tmpdir = require('../common/tmpdir');

function decode(value, encoding) {
  if (encoding === 'buffer') {
    assert.ok(Buffer.isBuffer(value));
    return value.toString();
  }
  assert.strictEqual(typeof value, 'string');
  return Buffer.from(value, encoding).toString();
}

const memoryVfs = vfs.create();
memoryVfs.writeFileSync('/file.txt', 'x');
memoryVfs.symlinkSync('file.txt', '/link');
memoryVfs.symlinkSync('/file.txt', '/abs-link');
const vfsInstances = [memoryVfs];

if (common.canCreateSymLink()) {
  tmpdir.refresh();
  fs.writeFileSync(tmpdir.resolve('file.txt'), 'x');
  fs.symlinkSync('file.txt', tmpdir.resolve('link'));
  // RealFSProvider returns an absolute target inside its root as a VFS path.
  fs.symlinkSync(tmpdir.resolve('file.txt'), tmpdir.resolve('abs-link'));
  vfsInstances.push(vfs.create(new vfs.RealFSProvider(tmpdir.path)));
}

const readlinks = [
  fs.readlinkSync,
  promisify(fs.readlink),
  fs.promises.readlink,
];
const realpaths = [
  fs.realpathSync,
  fs.realpathSync.native,
  promisify(fs.realpath),
  promisify(fs.realpath.native),
  fs.promises.realpath,
];
const mkdtemps = [
  fs.mkdtempSync,
  promisify(fs.mkdtemp),
  fs.promises.mkdtemp,
];

(async () => {
  for (const myVfs of vfsInstances) {
    const mountPoint = myVfs.mount();
    const prefix = path.join(mountPoint, 'tmp-');
    const vfsReadlinks = [
      myVfs.readlinkSync.bind(myVfs),
      promisify(myVfs.readlink.bind(myVfs)),
      myVfs.promises.readlink,
    ];
    const vfsRealpaths = [
      myVfs.realpathSync.bind(myVfs),
      promisify(myVfs.realpath.bind(myVfs)),
      myVfs.promises.realpath,
    ];

    for (const options of ['buffer', { encoding: 'buffer' }, 'hex', { encoding: 'hex' }]) {
      const encoding = options.encoding ?? options;

      for (const name of ['link', 'abs-link']) {
        const link = path.join(mountPoint, name);
        for (const readlink of [...readlinks, ...vfsReadlinks]) {
          assert.strictEqual(decode(await readlink(link, options), encoding),
                             fs.readlinkSync(link));
        }
        for (const realpath of [...realpaths, ...vfsRealpaths]) {
          assert.strictEqual(decode(await realpath(link, options), encoding),
                             fs.realpathSync(link));
        }
      }

      for (const mkdtemp of mkdtemps) {
        const dir = decode(await mkdtemp(prefix, options), encoding);
        assert.ok(dir.startsWith(prefix));
        assert.ok(fs.statSync(dir).isDirectory());
      }
    }

    for (const mkdtemp of mkdtemps) {
      const dir = await mkdtemp(Buffer.from(prefix), 'hex');
      assert.ok(Buffer.isBuffer(dir));
      assert.ok(fs.statSync(dir).isDirectory());
    }

    myVfs.unmount();
  }
})().then(common.mustCall());