From 61a1f5e02034ee4deac3e8bbe66c6eba2d600377 Mon Sep 17 00:00:00 2001 From: David Humphrey Date: Tue, 4 Dec 2018 22:36:06 -0500 Subject: [PATCH] Fix #603: add import/export provider to work with JSON fs images --- tests/index.js | 1 + tests/lib/import-export-provider.js | 36 ++++ tests/lib/test-utils.js | 13 +- .../providers/import-export-provider.spec.js | 203 ++++++++++++++++++ 4 files changed, 252 insertions(+), 1 deletion(-) create mode 100644 tests/lib/import-export-provider.js create mode 100644 tests/spec/providers/import-export-provider.spec.js diff --git a/tests/index.js b/tests/index.js index 8a7e136..03c78e2 100644 --- a/tests/index.js +++ b/tests/index.js @@ -51,6 +51,7 @@ require('./spec/providers/providers.spec'); require('./spec/providers/providers.indexeddb.spec'); require('./spec/providers/providers.websql.spec'); require('./spec/providers/providers.memory.spec'); +require('./spec/providers/import-export-provider.spec'); // Filer.FileSystemShell.* require('./spec/shell/cd.spec'); diff --git a/tests/lib/import-export-provider.js b/tests/lib/import-export-provider.js new file mode 100644 index 0000000..426428c --- /dev/null +++ b/tests/lib/import-export-provider.js @@ -0,0 +1,36 @@ +const MemoryProvider = require('../../src/providers/memory'); +const { parseBJSON } = require('../lib/test-utils'); + +class ImportExportProvider extends MemoryProvider { + + constructor(name, jsonImage) { + super(name); + this.unparsedJSONImage = jsonImage; + } + + /** + * In addition to the usual setup of a Memory provider, + * also parse and overwrite the internal database. + */ + open(callback) { + super.open(err => { + if(err) { + return callback(err); + } + + try { + this.db = parseBJSON(this.unparsedJSONImage); + this.unparsedJSONImage = null; + callback(); + } catch(e) { + callback(new Error(`unable to parse JSON filesystem image: ${e.message}`)); + } + }); + } + + export() { + return JSON.stringify(this.db); + } +} + +module.exports = ImportExportProvider; diff --git a/tests/lib/test-utils.js b/tests/lib/test-utils.js index 02a4dcc..8b8f904 100644 --- a/tests/lib/test-utils.js +++ b/tests/lib/test-utils.js @@ -140,6 +140,16 @@ function typedArrayEqual(a, b) { return true; } +/** + * Parse JSON with serialized Buffers + */ +const parseBJSON = json => + JSON.parse(json, (key, value) => + value && value.type === 'Buffer' ? + Buffer.from(value.data) : + value + ); + module.exports = { uniqueName: uniqueName, setup: setup, @@ -152,5 +162,6 @@ module.exports = { Memory: MemoryTestProvider }, cleanup: cleanup, - typedArrayEqual: typedArrayEqual + typedArrayEqual: typedArrayEqual, + parseBJSON }; diff --git a/tests/spec/providers/import-export-provider.spec.js b/tests/spec/providers/import-export-provider.spec.js new file mode 100644 index 0000000..8c13c73 --- /dev/null +++ b/tests/spec/providers/import-export-provider.spec.js @@ -0,0 +1,203 @@ +var { uniqueName, parseBJSON } = require('../../lib/test-utils.js'); +var ImportExportProvider = require('../../lib/import-export-provider'); +var expect = require('chai').expect; + +describe('Filer Provider Tests for ImportExportProvider', function() { + var provider; + + /** + * Simulate the sort of data a filesystem image would have + */ + var image = { + 'c20b2b02-81f5-43af-b117-78bc0affd1b7': { + name: 'file', + size: 36, + mtime: Date.now() + }, + '85ad404e-c29b-4c63-bb21-69655375d367': Buffer.from('data') + }; + function buildImageJSON() { + return JSON.stringify(image); + } + + beforeEach(function() { + provider = new ImportExportProvider(uniqueName(), buildImageJSON()); + }); + + afterEach(function() { + provider = null; + }); + + it('has open, getReadOnlyContext, and getReadWriteContext instance methods', function() { + expect(provider.open).to.be.a('function'); + expect(provider.getReadOnlyContext).to.be.a('function'); + expect(provider.getReadWriteContext).to.be.a('function'); + }); + + it('should open a new provider database', function(done) { + provider.open(function(error) { + expect(error).not.to.exist; + done(); + }); + }); + + it('should have expected Object type from imported image', function(done) { + provider.open(function(error) { + if (error) throw error; + + var context = provider.getReadWriteContext(); + context.getObject('c20b2b02-81f5-43af-b117-78bc0affd1b7', function(error, o) { + if (error) throw error; + + expect(o).to.deep.equal(image['c20b2b02-81f5-43af-b117-78bc0affd1b7']); + done(); + }); + }); + }); + + it('should have expected Buffer type from imported image', function(done) { + provider.open(function(error) { + if (error) throw error; + + var context = provider.getReadWriteContext(); + context.getObject('85ad404e-c29b-4c63-bb21-69655375d367', function(error, o) { + if (error) throw error; + + expect(o).to.deep.equal(image['85ad404e-c29b-4c63-bb21-69655375d367']); + done(); + }); + }); + }); + + it('should allow exporting the filesystem image', function(done) { + provider.open(function(error) { + if (error) throw error; + + var bjson = provider.export(); + var exported = parseBJSON(bjson); + + expect(exported['c20b2b02-81f5-43af-b117-78bc0affd1b7']).to.deep.equal(image['c20b2b02-81f5-43af-b117-78bc0affd1b7']); + expect(exported['85ad404e-c29b-4c63-bb21-69655375d367']).to.deep.equal(image['85ad404e-c29b-4c63-bb21-69655375d367']); + + done(); + }); + }); + + it('should allow putObject() and getObject()', function(done) { + provider.open(function(error) { + if (error) throw error; + + var context = provider.getReadWriteContext(); + // Simple JS Object + var value = { + a: 'a', + b: 1, + c: true, + d: [1, 2, 3], + e: { + e1: ['a', 'b', 'c'] + } + }; + context.putObject('key', value, function(error) { + if (error) throw error; + + context.getObject('key', function(error, result) { + expect(error).not.to.exist; + expect(result).to.be.an('object'); + expect(result).to.deep.equal(value); + done(); + }); + }); + }); + }); + + it('should allow putBuffer() and getBuffer()', function(done) { + provider.open(function(error) { + if (error) throw error; + + var context = provider.getReadWriteContext(); + var buf = Buffer.from([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]); + context.putBuffer('key', buf, function(error) { + if (error) throw error; + + context.getBuffer('key', function(error, result) { + expect(error).not.to.exist; + expect(Buffer.isBuffer(result)).to.be.true; + expect(result).to.deep.equal(buf); + done(); + }); + }); + }); + }); + + it('should allow zero-length Buffers with putBuffer() and getBuffer()', function(done) { + provider.open(function(error) { + if (error) throw error; + + var context = provider.getReadWriteContext(); + // Zero-length Buffer + var buf = Buffer.alloc(0); + context.putBuffer('key', buf, function(error) { + if (error) throw error; + + context.getBuffer('key', function(error, result) { + expect(error).not.to.exist; + expect(Buffer.isBuffer(result)).to.be.true; + expect(result).to.deep.equal(buf); + done(); + }); + }); + }); + }); + + it('should allow delete()', function(done) { + provider.open(function(error) { + if (error) throw error; + + var context = provider.getReadWriteContext(); + context.putObject('key', 'value', function(error) { + if (error) throw error; + + context.delete('key', function(error) { + if (error) throw error; + + context.getObject('key', function(error, result) { + expect(error).not.to.exist; + expect(result).not.to.exist; + done(); + }); + }); + }); + }); + }); + + it('should allow clear()', function(done) { + provider.open(function(error) { + if (error) throw error; + + var context = provider.getReadWriteContext(); + context.putObject('key1', 'value1', function(error) { + if (error) throw error; + + context.putObject('key2', 'value2', function(error) { + if (error) throw error; + + context.clear(function(error) { + if (error) throw error; + + context.getObject('key1', function(error, result) { + if (error) throw error; + expect(result).not.to.exist; + + context.getObject('key2', function(error, result) { + if (error) throw error; + expect(result).not.to.exist; + done(); + }); + }); + }); + }); + }); + }); + }); +});