From 747a8e707b8fa0e628a728b169e19ca759132312 Mon Sep 17 00:00:00 2001 From: Braydon Fuller Date: Thu, 14 Mar 2019 10:28:51 -0700 Subject: [PATCH] blockstore: tests and fixes for blockstore error cases --- lib/blockstore/file.js | 11 +++- test/blockstore-test.js | 136 +++++++++++++++++++++++++++++++++++++++- 2 files changed, 143 insertions(+), 4 deletions(-) diff --git a/lib/blockstore/file.js b/lib/blockstore/file.js index 84d2f856..fd31bf76 100644 --- a/lib/blockstore/file.js +++ b/lib/blockstore/file.js @@ -47,6 +47,9 @@ class FileBlockStore extends AbstractBlockStore { this.maxFileLength = options.maxFileLength || 128 * 1024 * 1024; + assert(Number.isSafeInteger(this.maxFileLength), + 'Invalid max file length.'); + this.network = Network.primary; if (options.network != null) @@ -319,11 +322,15 @@ class FileBlockStore extends AbstractBlockStore { await fs.close(fd); - if (mwritten !== mlength) + if (mwritten !== mlength) { + this.writing = false; throw new Error('Could not write block magic.'); + } - if (bwritten !== blength) + if (bwritten !== blength) { + this.writing = false; throw new Error('Could not write block.'); + } filerecord.blocks += 1; filerecord.used += length; diff --git a/test/blockstore-test.js b/test/blockstore-test.js index eddd756b..dd84f159 100644 --- a/test/blockstore-test.js +++ b/test/blockstore-test.js @@ -64,8 +64,9 @@ describe('BlockStore', function() { }); it('has unimplemented base methods', async () => { - const methods = ['open', 'close', 'write', 'read', - 'prune', 'has']; + const methods = ['open', 'close', 'write', 'writeUndo', + 'read', 'readUndo', 'prune', 'pruneUndo', + 'has', 'hasUndo', 'ensure']; const store = new AbstractBlockStore(); @@ -277,6 +278,40 @@ describe('BlockStore', function() { }); }); + describe('constructor', function() { + it('will error with invalid location', () => { + let err = null; + + try { + new FileBlockStore({ + location: 'tmp/.bcoin/blocks', + maxFileLength: 1024 + }); + } catch (e) { + err = e; + } + + assert(err); + assert.equal(err.message, 'Location not absolute.'); + }); + + it('will error with invalid max file length', () => { + let err = null; + + try { + new FileBlockStore({ + location: location, + maxFileLength: 'notanumber' + }); + } catch (e) { + err = e; + } + + assert(err); + assert.equal(err.message, 'Invalid max file length.'); + }); + }); + describe('allocate', function() { it('will fail with length above file max', async () => { let err = null; @@ -337,6 +372,103 @@ describe('BlockStore', function() { const filepath = store.filepath(types.UNDO, 99999); assert.equal(filepath, '/tmp/.bcoin/blocks/blu99999.dat'); }); + + it('will fail for unknown prefix', () => { + let err = null; + try { + store.filepath(0, 1234); + } catch (e) { + err = e; + } + + assert(err); + assert.equal(err.message, 'Unknown file prefix.'); + }); + }); + + describe('write', function() { + const write = fs.write; + const open = fs.open; + const close = fs.close; + let allocate = null; + + beforeEach(() => { + allocate = store.allocate; + }); + + afterEach(() => { + // Restore stubbed methods. + fs.write = write; + fs.open = open; + fs.close = close; + store.allocate = allocate; + }); + + it('will error if total magic bytes not written', async () => { + let err = null; + + store.allocate = () => { + return { + fileno: 20, + filerecord: { + used: 0 + }, + filepath: '/tmp/.bcoin/blocks/blk00020.dat' + }; + }; + fs.open = () => 7; + fs.close = () => undefined; + fs.write = () => 0; + + try { + const hash = random.randomBytes(128); + const block = random.randomBytes(32); + await store.write(hash, block); + } catch (e) { + err = e; + } + + assert(err, 'Expected error.'); + assert.equal(err.message, 'Could not write block magic.'); + }); + + it('will error if total block bytes not written', async () => { + let err = 0; + + let called = 0; + store.allocate = () => { + return { + fileno: 20, + filerecord: { + used: 0 + }, + filepath: '/tmp/.bcoin/blocks/blk00020.dat' + }; + }; + fs.open = () => 7; + fs.close = () => undefined; + fs.write = (fd, buffer, offset, length, position) => { + let written = 0; + + if (called === 0) + written = length; + + called += 1; + + return written; + }; + + try { + const hash = random.randomBytes(128); + const block = random.randomBytes(32); + await store.write(hash, block); + } catch (e) { + err = e; + } + + assert(err, 'Expected error.'); + assert.equal(err.message, 'Could not write block.'); + }); }); });