javascript - What is causing "Uncaught Error: write after end" in my tests? -
i have following code:
var promise = require('bluebird'); promise.longstacktraces(); var path = require('path'); var fs = promise.promisifyall(require('fs-extra')); var clone = require('nodegit').clone.clone; var tar = require('tar-fs'); var zlib = require('zlib'); var gzip = zlib.creategzip(); var globasync = promise.promisify(require('glob')); module.exports = archive; function archive(pkg) { var self = this; var tmp_dir_name = '.tmp'; var code_dir_name = 'code'; var files_dir_name = 'files'; var output_dir_name = 'archives'; var coverall_docs_dir_name = 'coverall_documents'; // archive's name (no extension): self.name = pkg.name; self.recipient_name = pkg.recipient_name; // path letter.tex: self.tex_letter_path = path.resolve(pkg.files.letter); // path resume.tex: self.tex_resume_path = path.resolve(pkg.files.resume); // path merged.pdf (letter.pdf + resume.pdf): self.pdf_package_path = path.resolve(pkg.compiled_files.package); // temp dir archive assembled: self.tmp_path = path.resolve(tmp_dir_name, pkg.name); // path final archive: self.output_path = path.resolve(output_dir_name, self.name + '.tar.gz'); // copy files added archive: self.files_path = path.resolve(tmp_dir_name, self.name, files_dir_name); // tex files within archive: self.coverall_docs_path = path.resolve(self.files_path, code_dir_name, coverall_docs_dir_name); } archive.prototype.make = promise.method(function() { var self = this; return self._preparefilesdir() .then(self._copyfiles.bind(self)) .then(self._writearchive.bind(self)) .then(self._deltmpdir.bind(self)); }); // ******************************** // * private functions // ******************************** archive.prototype._preparefilesdir = function() { var self = this; return fs.emptydirasync(self.tmp_path); }; archive.prototype._copyfiles = function() { var self = this; var sources = { tex_letter_path: path.resolve(self.tex_letter_path, '..'), tex_resume_path: path.resolve(self.tex_resume_path, '..'), tex_letter_shared_path: path.resolve(self.tex_letter_path, '../../shared'), pdf_package_path: self.pdf_package_path }; var destinations = { letter_path: path.resolve(self.coverall_docs_path, 'coverletters', self.recipient_name.tolowercase()), resume_path: path.resolve(self.coverall_docs_path, 'resume'), letter_shared_path: path.resolve(self.coverall_docs_path, 'coverletters/shared'), pdf_package_path: path.resolve(self.files_path, 'pdf', self.recipient_name.tolowercase() + '.pdf'), coverall_repo_path: path.resolve(self.files_path, 'code/coverall') }; var filters = { tex: function(filename) { var contains_dot = /\./gm; var hidden = /\/\./gm; var cls_or_tex_file = /\.(cls|tex)$/gm; var is_a_dir = !contains_dot.test(filename); var is_not_hidden = (contains_dot.test(filename) && !hidden.test(filename)); var is_cls_or_tex = cls_or_tex_file.test(filename); // doesn't contain dot or isn't hidden file or cls/tex file var is_allowed = is_a_dir || is_not_hidden || is_cls_or_tex; return is_allowed; }, pdf: /[^\.].*\.pdf/ }; var copyletter = function() { return fs.copyasync(sources.tex_letter_path, destinations.letter_path, { filter: filters.tex }); }; function copyshared() { return fs.copyasync(sources.tex_letter_shared_path, destinations.letter_shared_path, { filter: filters.tex }); } function copyresume() { return fs.copyasync(sources.tex_resume_path, destinations.resume_path, { filter: filters.tex }); } function copypdf() { return fs.copyasync(sources.pdf_package_path, destinations.pdf_package_path, { filter: filters.pdf }); } function copyjs() { return clone('https://github.com/coaxial/coverall.git', destinations.coverall_repo_path); } return promise.all([ copyletter(), copyshared(), copyresume(), copypdf(), copyjs() ]); }; archive.prototype._writearchive = function() { var self = this; var archive_dir_path = path.resolve(self.output_path, '..'); var tarpromise = function() { return new promise(function(resolve, reject) { tar.pack(self.files_path) .pipe(gzip) .pipe(fs.createwritestream(self.output_path)) .on('error', reject) .on('finish', resolve); }); }; return fs.ensuredirasync(archive_dir_path) .then(tarpromise); }; archive.prototype._deltmpdir = function() { var self = this; return fs.removeasync(self.tmp_path); };
and testing with:
/*eslint-env mocha */ var chai = require('chai'); var chaiaspromised = require("chai-as-promised"); var expect = chai.expect; var promise = require('bluebird'); promise.longstacktraces(); var archive = require('../lib/archive'); var path = require('path'); var fs = promise.promisifyall(require('fs-extra')); var globasync = promise.promisify(require('glob')); var tar = require('tar-fs'); var zlib = promise.promisifyall(require('zlib')); var _ = require('lodash'); chai.use(chaiaspromised); describe.only('archive', function() { var pkg; beforeeach(function() { pkg = { name: 'test_0790feebb1', recipient_name: 'test', files: { letter: '../coverall_documents/coverletters/test/letter.tex', resume: '../coverall_documents/resume/resume.tex' }, compiled_files: { package: '../coverall_documents/coverletters/test/test.pdf' } }; }); // after(function() { // return promise.all([ // 'archives/test*', // 'test/.tmp' // ].map(function(glob_pattern) { // return globasync(glob_pattern) // .each(function(filename) { // // make every file writeable git packfiles can removed // return fs.chmodasync(filename, '755') // .then(function() { fs.removeasync(filename); }); // }) // })); // }); describe('#make', function() { it('creates archive', function() { var modified_pkg = _.clonedeep(pkg); modified_pkg.name = 'test_0000000001'; var archive_location = path.resolve('archives', modified_pkg.name + '.tar.gz'); var test_archive = new archive(modified_pkg); return test_archive.make() .then(function() { return fs.statasync(archive_location); }) .then(function(file) { return expect(file).to.exist; }) .catch(function(e) { return expect(e).to.not.exist; }); }); it('creates gzip compressed archive', function() { var modified_pkg = _.clonedeep(pkg); modified_pkg.name = 'test_0000000002'; var archive_location = path.resolve('archives', modified_pkg.name + '.tar.gz'); var test_archive = new archive(modified_pkg); // inspired https://github.com/mafintosh/gunzip-maybe/blob/master/index.js#l6-l11 var isgzipped = function(data) { var gzip_magic_bytes = [0x1f, 0x8b]; var deflate_compression_method = 0x08; var buffer = data[1]; if (buffer[0] !== gzip_magic_bytes[0] && buffer[1] !== gzip_magic_bytes[1]) return false; if (buffer[2] !== deflate_compression_method) return false; return true; }; return test_archive.make() .then(function() { return fs.openasync(archive_location, 'r'); }) .then(function(fd) { var buffer = new buffer(10); var buffer_offset = 0; var buffer_length = 10; var file_position = 0; return fs.readasync(fd, buffer, buffer_offset, buffer_length, file_position); }) .then(function(data) { console.log('data', data); return data; }) .then(function(data) { return expect(isgzipped(data)).to.be.true; }) }); it('has correct directory structure', function() { var modified_pkg = _.clonedeep(pkg); modified_pkg.name = 'test_0000000003'; var archive_location = path.resolve('archives', modified_pkg.name + '.tar.gz'); var test_archive = new archive(modified_pkg); var tmp_extract_path = path.resolve('test/.tmp'); var tarpromise = function(archive_path) { return new promise(function(resolve, reject) { fs.createreadstream(archive_path) .pipe(zlib.unzip()) .pipe(tar.extract(tmp_extract_path)) .on('error', reject) .on('finish', resolve); }) }; var verifydir = function() { return promise.all([ 'code', 'pdf', 'code/coverall', 'code/coverall_documents', 'code/coverall_documents/coverletters', 'code/coverall_documents/coverletters/test', 'code/coverall_documents/coverletters/shared', 'code/coverall_documents/resume', 'code/coverall_documents/coverletters' ].map(function(subpath) { return expect(fs.statasync(path.resolve(tmp_extract_path, subpath))) .to.be.fulfilled; })) }; return test_archive.make() .then(function() { return tarpromise(archive_location); }) .then(function() { return verifydir(); }); }); it('removes temporary dir', function() { var modified_pkg = _.clonedeep(pkg); modified_pkg.name = 'test_0000000004'; var archive_location = path.resolve('archives', modified_pkg.name + '.tar.gz'); var test_archive = new archive(modified_pkg); var tmp_dir = path.resolve('.tmp'); return test_archive.make() .then(function() { return expect(fs.statasync(tmp_dir)).to.be.rejected; }); }); }); });
which results in:
$ mocha test archive #make ✓ creates archive (644ms) 1) creates gzip compressed archive 2) has correct directory structure 3) removes temporary dir 1 passing (2s) 3 failing 1) archive #make creates gzip compressed archive: uncaught error: write after end @ writeafterend (_stream_writable.js:167:12) @ gzip.writable.write (_stream_writable.js:214:5) @ ondata (node_modules/tar-fs/node_modules/tar-stream/node_modules/readable-stream/lib/_stream_readable.js:574:20) @ readableaddchunk (node_modules/tar-fs/node_modules/tar-stream/node_modules/readable-stream/lib/_stream_readable.js:198:16) @ readable.push (node_modules/tar-fs/node_modules/tar-stream/node_modules/readable-stream/lib/_stream_readable.js:162:10) @ pack._encode (node_modules/tar-fs/node_modules/tar-stream/pack.js:154:17) @ pack.entry (node_modules/tar-fs/node_modules/tar-stream/pack.js:100:10) @ onstat (node_modules/tar-fs/index.js:108:19) @ node_modules/tar-fs/index.js:40:9 @ fsreqwrap.oncomplete (fs.js:95:15) 2) archive #make has correct directory structure: assertionerror: expected false true @ context.<anonymous> (test/archive_spec.js:96:10) 3) archive #make removes temporary dir: uncaught error: write after end @ writeafterend (_stream_writable.js:167:12) @ gzip.writable.write (_stream_writable.js:214:5) @ ondata (node_modules/tar-fs/node_modules/tar-stream/node_modules/readable-stream/lib/_stream_readable.js:574:20) @ readableaddchunk (node_modules/tar-fs/node_modules/tar-stream/node_modules/readable-stream/lib/_stream_readable.js:198:16) @ readable.push (node_modules/tar-fs/node_modules/tar-stream/node_modules/readable-stream/lib/_stream_readable.js:162:10) @ pack._encode (node_modules/tar-fs/node_modules/tar-stream/pack.js:154:17) @ pack.entry (node_modules/tar-fs/node_modules/tar-stream/pack.js:100:10) @ onstat (node_modules/tar-fs/index.js:108:19) @ node_modules/tar-fs/index.js:40:9 @ fsreqwrap.oncomplete (fs.js:95:15)
i suspected race condition commented out after
block see if make difference doesn't.
i not understand uncaught error: write after end
nor why stacktrace unusable, though using promise.longstacktraces()
. causing error?
my tests overly complicated doing , repeating code several times when instantiating different test_archive
objects. how refactor them?
you're trying re-use same gzip
instance, won't work. explains why first test works fine.
so move var gzip = zlib.creategzip();
line right inside archive.prototype._writearchive
function.
Comments
Post a Comment