2015-10-02 2 views
0

я следующий код:Что вызывает «Неудачная ошибка: напишите после конца» в моих тестах?

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'; 

    // the archive's name (no extension): 
    self.name = pkg.name; 
    self.recipient_name = pkg.recipient_name; 
    // path to letter.tex: 
    self.tex_letter_path = path.resolve(pkg.files.letter); 
    // path to resume.tex: 
    self.tex_resume_path = path.resolve(pkg.files.resume); 
    // path to merged.pdf (letter.pdf + resume.pdf): 
    self.pdf_package_path = path.resolve(pkg.compiled_files.package); 
    // temp dir where the archive is assembled: 
    self.tmp_path = path.resolve(tmp_dir_name, pkg.name); 
    // path to final archive: 
    self.output_path = path.resolve(output_dir_name, self.name + '.tar.gz'); 
    // where to copy files to be added to the archive: 
    self.files_path = path.resolve(tmp_dir_name, self.name, files_dir_name); 
    // where the tex files are within the 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); 
     // it doesn't contain a dot or it isn't a hidden file or it is a 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); 
}; 

и я тестирую его:

/*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 so the git packfiles can be removed 
    //   return fs.chmodAsync(filename, '755') 
    //   .then(function() { fs.removeAsync(filename); }); 
    //  }) 
    // })); 
    // }); 

    describe('#make', function() { 
    it('creates an 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 a 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 from 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 the 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 the 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; }); 
    }); 
    }); 
}); 

Какие результаты в:

$ mocha test 


    Archive 
    #make 
     ✓ creates an archive (644ms) 
     1) creates a gzip compressed archive 
     2) has the correct directory structure 
     3) removes the temporary dir 


    1 passing (2s) 
    3 failing 

    1) Archive #make creates a gzip compressed archive: 
    Uncaught Error: write after end 
     at writeAfterEnd (_stream_writable.js:167:12) 
     at Gzip.Writable.write (_stream_writable.js:214:5) 
     at ondata (node_modules/tar-fs/node_modules/tar-stream/node_modules/readable-stream/lib/_stream_readable.js:574:20) 
     at readableAddChunk (node_modules/tar-fs/node_modules/tar-stream/node_modules/readable-stream/lib/_stream_readable.js:198:16) 
     at Readable.push (node_modules/tar-fs/node_modules/tar-stream/node_modules/readable-stream/lib/_stream_readable.js:162:10) 
     at Pack._encode (node_modules/tar-fs/node_modules/tar-stream/pack.js:154:17) 
     at Pack.entry (node_modules/tar-fs/node_modules/tar-stream/pack.js:100:10) 
     at onstat (node_modules/tar-fs/index.js:108:19) 
     at node_modules/tar-fs/index.js:40:9 
     at FSReqWrap.oncomplete (fs.js:95:15) 

    2) Archive #make has the correct directory structure: 
    AssertionError: expected false to be true 
     at Context.<anonymous> (test/archive_spec.js:96:10) 

    3) Archive #make removes the temporary dir: 
    Uncaught Error: write after end 
     at writeAfterEnd (_stream_writable.js:167:12) 
     at Gzip.Writable.write (_stream_writable.js:214:5) 
     at ondata (node_modules/tar-fs/node_modules/tar-stream/node_modules/readable-stream/lib/_stream_readable.js:574:20) 
     at readableAddChunk (node_modules/tar-fs/node_modules/tar-stream/node_modules/readable-stream/lib/_stream_readable.js:198:16) 
     at Readable.push (node_modules/tar-fs/node_modules/tar-stream/node_modules/readable-stream/lib/_stream_readable.js:162:10) 
     at Pack._encode (node_modules/tar-fs/node_modules/tar-stream/pack.js:154:17) 
     at Pack.entry (node_modules/tar-fs/node_modules/tar-stream/pack.js:100:10) 
     at onstat (node_modules/tar-fs/index.js:108:19) 
     at node_modules/tar-fs/index.js:40:9 
     at FSReqWrap.oncomplete (fs.js:95:15) 

Я подозревал, что состояние гонки, поэтому я закомментировал after блок, чтобы увидеть, будет ли это иметь какое-либо значение, но это не так.

Я не понимаю, что такое Uncaught Error: write after end, и почему stacktrace непригодна для использования, хотя я использую Promise.longStackTraces(). Что вызывает эту ошибку?

Мои тесты выглядят слишком сложными для того, что они делают, и я повторяю код несколько раз при создании экземпляров различных объектов test_archive. Как я могу их реорганизовать?

+0

Последний абзац подходит для Code Review, но не SO - и это абсолютно отдельная проблема. –

ответ

3

Вы пытаетесь повторно использовать тот же экземпляр gzip, который не будет работать. Это также объясняет, почему первый тест работает просто отлично.

Итак, переместите свою линию var gzip = zlib.createGzip(); вправо внутри вашей функции Archive.prototype._writeArchive.

+0

Спасибо, это было. Документация по zlib очень короткая, я не понимал, что это создает и повторно использует один и тот же экземпляр. – springloaded

Смежные вопросы