From 18b17bda7ca812b0f83f688993808f4aa98932f8 Mon Sep 17 00:00:00 2001 From: Enishowk Date: Tue, 4 Dec 2018 16:36:10 +0100 Subject: [PATCH] feat(@xen-orchestra/fs): add unit tests (#3736) --- .env.example | 2 + .gitignore | 1 + @xen-orchestra/fs/package.json | 1 + @xen-orchestra/fs/src/fs.spec.js | 155 +++++++++++++++++++++++++++++++ @xen-orchestra/fs/src/nfs.js | 8 +- yarn.lock | 5 + 6 files changed, 171 insertions(+), 1 deletion(-) create mode 100644 .env.example create mode 100644 @xen-orchestra/fs/src/fs.spec.js diff --git a/.env.example b/.env.example new file mode 100644 index 000000000..5ba5e8b1f --- /dev/null +++ b/.env.example @@ -0,0 +1,2 @@ +# xo_fs_nfs=nfs://ip:/folder +# xo_fs_smb=smb://login:pass@domain\\ip\folder diff --git a/.gitignore b/.gitignore index 9071a5419..0c2eaca0f 100644 --- a/.gitignore +++ b/.gitignore @@ -30,3 +30,4 @@ pnpm-debug.log pnpm-debug.log.* yarn-error.log yarn-error.log.* +.env diff --git a/@xen-orchestra/fs/package.json b/@xen-orchestra/fs/package.json index c3d8f48c3..3c0efdc8a 100644 --- a/@xen-orchestra/fs/package.json +++ b/@xen-orchestra/fs/package.json @@ -42,6 +42,7 @@ "@babel/preset-flow": "^7.0.0", "babel-plugin-lodash": "^3.3.2", "cross-env": "^5.1.3", + "dotenv": "^6.1.0", "index-modules": "^0.3.0", "rimraf": "^2.6.2" }, diff --git a/@xen-orchestra/fs/src/fs.spec.js b/@xen-orchestra/fs/src/fs.spec.js new file mode 100644 index 000000000..ea2e40d77 --- /dev/null +++ b/@xen-orchestra/fs/src/fs.spec.js @@ -0,0 +1,155 @@ +/* eslint-env jest */ + +import 'dotenv/config' +import getStream from 'get-stream' +import { tmpdir } from 'os' + +import { getHandler } from '.' + +// https://gist.github.com/julien-f/3228c3f34fdac01ade09 +const unsecureRandomBytes = n => { + const bytes = Buffer.alloc(n) + + const odd = n & 1 + for (let i = 0, m = n - odd; i < m; i += 2) { + bytes.writeUInt16BE((Math.random() * 65536) | 0, i) + } + + if (odd) { + bytes.writeUInt8((Math.random() * 256) | 0, n - 1) + } + + return bytes +} + +const TEST_DATA = unsecureRandomBytes(1024) + +const rejectionOf = p => + p.then( + value => { + throw value + }, + reason => reason + ) + +const handlers = [`file://${tmpdir()}`] +if (process.env.xo_fs_nfs) handlers.push(process.env.xo_fs_nfs) +if (process.env.xo_fs_smb) handlers.push(process.env.xo_fs_smb) + +handlers.forEach(url => { + describe(url, () => { + let handler + const testDir = `xo-fs-tests-${Date.now()}` + const testFile = `${testDir}/file` + + beforeAll(async () => { + handler = getHandler({ url }) + await handler.sync() + }) + afterAll(async () => { + await handler.forget() + handler = undefined + }) + + afterEach(async () => { + await handler.rmdir(testDir, { recursive: true }).catch(error => { + if (error.code !== 'ENOENT') { + throw error + } + }) + }) + + describe('#test()', () => { + it('tests the remote appears to be working', async () => { + expect(await handler.test()).toEqual({ + success: true, + }) + }) + }) + + describe('#outputFile()', () => { + it('writes data to a file', async () => { + await handler.outputFile(testFile, TEST_DATA) + expect(await handler.readFile(testFile)).toEqual(TEST_DATA) + }) + + it('throws on existing files', async () => { + await handler.outputFile(testFile, '') + const error = await rejectionOf(handler.outputFile(testFile, '')) + expect(error.code).toBe('EEXIST') + }) + }) + + describe('#readFile', () => { + it('returns a buffer containing the contents of the file', async () => { + await handler.outputFile(testFile, TEST_DATA) + expect(await handler.readFile(testFile)).toEqual(TEST_DATA) + }) + + it('throws on missing file', async () => { + const error = await rejectionOf(handler.readFile(testFile)) + expect(error.code).toBe('ENOENT') + }) + }) + + describe('#list()', () => { + it(`should list the content of folder`, async () => { + await handler.outputFile(testFile, TEST_DATA) + await expect(await handler.list(testDir)).toEqual(['file']) + }) + }) + + describe('#createReadStream()', () => { + it(`should return a stream`, async () => { + await handler.outputFile(testFile, TEST_DATA) + const buffer = await getStream.buffer( + await handler.createReadStream(testFile) + ) + + await expect(buffer).toEqual(TEST_DATA) + }) + }) + describe('#getSize()', () => { + it(`should return the correct size`, async () => { + await handler.outputFile(testFile, TEST_DATA) + expect(await handler.getSize(testFile)).toEqual(TEST_DATA.length) + }) + }) + + describe('#rename()', () => { + it(`should rename the file`, async () => { + await handler.outputFile(testFile, TEST_DATA) + await handler.rename(testFile, `${testDir}/file2`) + + expect(await handler.list(testDir)).toEqual(['file2']) + expect(await handler.readFile(`${testDir}/file2`)).toEqual(TEST_DATA) + }) + }) + + describe('#unlink()', () => { + it(`should remove the file`, async () => { + await handler.outputFile(testFile, TEST_DATA) + await handler.unlink(testFile) + + await expect(await handler.list(testDir)).toEqual([]) + }) + }) + + describe('#rmdir()', () => { + it(`should remove folder resursively`, async () => { + await handler.outputFile(testFile, TEST_DATA) + await handler.rmdir(testDir, { recursive: true }) + + const error = await rejectionOf(handler.list(testDir)) + expect(error.code).toBe('ENOENT') + }) + + it(`should throw an error when recursive is false`, async () => { + await handler.outputFile(testFile, TEST_DATA) + + const error = await rejectionOf(handler.rmdir(testDir)) + await expect(error.code).toEqual('ENOTEMPTY') + }) + }) + }) +}) diff --git a/@xen-orchestra/fs/src/nfs.js b/@xen-orchestra/fs/src/nfs.js index d6e38b34b..48f75cac9 100644 --- a/@xen-orchestra/fs/src/nfs.js +++ b/@xen-orchestra/fs/src/nfs.js @@ -14,7 +14,13 @@ export default class NfsHandler extends LocalHandler { ) { super(remote, opts) - this._realPath = join(mountsDir, remote.id) + this._realPath = join( + mountsDir, + remote.id || + Math.random() + .toString(36) + .slice(2) + ) } get type() { diff --git a/yarn.lock b/yarn.lock index f0bd59095..05ab42c8a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4498,6 +4498,11 @@ dot-prop@^4.1.1: dependencies: is-obj "^1.0.0" +dotenv@^6.1.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-6.1.0.tgz#9853b6ca98292acb7dec67a95018fa40bccff42c" + integrity sha512-/veDn2ztgRlB7gKmE3i9f6CmDIyXAy6d5nBq+whO9SLX+Zs1sXEgFLPi+aSuWqUuusMfbi84fT8j34fs1HaYUw== + double-ended-queue@^2.1.0-0: version "2.1.0-0" resolved "https://registry.yarnpkg.com/double-ended-queue/-/double-ended-queue-2.1.0-0.tgz#103d3527fd31528f40188130c841efdd78264e5c"