spr*_*ded 0 javascript mocha.js node.js promise chai
我有以下代码:
\n\nvar Promise = require(\'bluebird\');\nPromise.longStackTraces();\nvar path = require(\'path\');\nvar fs = Promise.promisifyAll(require(\'fs-extra\'));\nvar clone = require(\'nodegit\').Clone.clone;\nvar tar = require(\'tar-fs\');\nvar zlib = require(\'zlib\');\nvar gzip = zlib.createGzip();\nvar globAsync = Promise.promisify(require(\'glob\'));\n\nmodule.exports = Archive;\n\nfunction Archive(pkg) {\n var self = this;\n var tmp_dir_name = \'.tmp\';\n var code_dir_name = \'code\';\n var files_dir_name = \'files\';\n var output_dir_name = \'archives\';\n var coverall_docs_dir_name = \'coverall_documents\';\n\n // the archive\'s name (no extension):\n self.name = pkg.name;\n self.recipient_name = pkg.recipient_name;\n // path to letter.tex:\n self.tex_letter_path = path.resolve(pkg.files.letter);\n // path to resume.tex:\n self.tex_resume_path = path.resolve(pkg.files.resume);\n // path to merged.pdf (letter.pdf + resume.pdf):\n self.pdf_package_path = path.resolve(pkg.compiled_files.package);\n // temp dir where the archive is assembled:\n self.tmp_path = path.resolve(tmp_dir_name, pkg.name);\n // path to final archive:\n self.output_path = path.resolve(output_dir_name, self.name + \'.tar.gz\');\n // where to copy files to be added to the archive:\n self.files_path = path.resolve(tmp_dir_name, self.name, files_dir_name);\n // where the tex files are within the archive:\n self.coverall_docs_path = path.resolve(self.files_path, code_dir_name, coverall_docs_dir_name);\n}\n\nArchive.prototype.make = Promise.method(function() {\n var self = this;\n return self._prepareFilesDir()\n .then(self._copyFiles.bind(self))\n .then(self._writeArchive.bind(self))\n .then(self._delTmpDir.bind(self));\n});\n\n// ********************************\n// * Private functions\n// ********************************\n\nArchive.prototype._prepareFilesDir = function() {\n var self = this;\n return fs.emptyDirAsync(self.tmp_path);\n};\n\nArchive.prototype._copyFiles = function() {\n var self = this;\n var sources = {\n tex_letter_path: path.resolve(self.tex_letter_path, \'..\'),\n tex_resume_path: path.resolve(self.tex_resume_path, \'..\'),\n tex_letter_shared_path: path.resolve(self.tex_letter_path, \'../../shared\'),\n pdf_package_path: self.pdf_package_path\n };\n var destinations = {\n letter_path: path.resolve(self.coverall_docs_path, \'coverletters\', self.recipient_name.toLowerCase()),\n resume_path: path.resolve(self.coverall_docs_path, \'resume\'),\n letter_shared_path: path.resolve(self.coverall_docs_path, \'coverletters/shared\'),\n pdf_package_path: path.resolve(self.files_path, \'pdf\', self.recipient_name.toLowerCase() + \'.pdf\'),\n coverall_repo_path: path.resolve(self.files_path, \'code/coverall\')\n };\n var filters = {\n tex: function(filename) {\n var contains_dot = /\\./gm;\n var hidden = /\\/\\./gm;\n var cls_or_tex_file = /\\.(cls|tex)$/gm;\n var is_a_dir = !contains_dot.test(filename);\n var is_not_hidden = (contains_dot.test(filename) && !hidden.test(filename));\n var is_cls_or_tex = cls_or_tex_file.test(filename);\n // it doesn\'t contain a dot or it isn\'t a hidden file or it is a cls/tex file\n var is_allowed = is_a_dir || is_not_hidden || is_cls_or_tex;\n return is_allowed;\n },\n pdf: /[^\\.].*\\.pdf/\n };\n\n var copyLetter = function() {\n return fs.copyAsync(sources.tex_letter_path, destinations.letter_path, { filter: filters.tex });\n };\n function copyShared() {\n return fs.copyAsync(sources.tex_letter_shared_path, destinations.letter_shared_path, { filter: filters.tex });\n }\n function copyResume() {\n return fs.copyAsync(sources.tex_resume_path, destinations.resume_path, { filter: filters.tex });\n }\n function copyPdf() {\n return fs.copyAsync(sources.pdf_package_path, destinations.pdf_package_path, { filter: filters.pdf });\n }\n function copyJs() {\n return clone(\'https://github.com/coaxial/coverall.git\', destinations.coverall_repo_path);\n }\n\n\n return Promise.all([\n copyLetter(),\n copyShared(),\n copyResume(),\n copyPdf(),\n copyJs()\n ]);\n};\n\nArchive.prototype._writeArchive = function() {\n var self = this;\n var archive_dir_path = path.resolve(self.output_path, \'..\');\n var tarPromise = function() {\n return new Promise(function(resolve, reject) {\n tar.pack(self.files_path)\n .pipe(gzip)\n .pipe(fs.createWriteStream(self.output_path))\n .on(\'error\', reject)\n .on(\'finish\', resolve);\n });\n };\n\n return fs.ensureDirAsync(archive_dir_path)\n .then(tarPromise);\n};\n\nArchive.prototype._delTmpDir = function() {\n var self = this;\n\n return fs.removeAsync(self.tmp_path);\n};\nRun Code Online (Sandbox Code Playgroud)\n\n我正在测试它:
\n\n/*eslint-env mocha */\nvar chai = require(\'chai\');\nvar chaiAsPromised = require("chai-as-promised");\nvar expect = chai.expect;\nvar Promise = require(\'bluebird\');\nPromise.longStackTraces();\nvar Archive = require(\'../lib/archive\');\nvar path = require(\'path\');\nvar fs = Promise.promisifyAll(require(\'fs-extra\'));\nvar globAsync = Promise.promisify(require(\'glob\'));\nvar tar = require(\'tar-fs\');\nvar zlib = Promise.promisifyAll(require(\'zlib\'));\nvar _ = require(\'lodash\');\n\nchai.use(chaiAsPromised);\n\ndescribe.only(\'Archive\', function() {\n var pkg;\n\n beforeEach(function() {\n pkg = {\n name: \'test_0790feebb1\',\n recipient_name: \'Test\',\n files: {\n letter: \'../coverall_documents/coverletters/test/letter.tex\',\n resume: \'../coverall_documents/resume/resume.tex\'\n },\n compiled_files: {\n package: \'../coverall_documents/coverletters/test/test.pdf\'\n }\n };\n });\n\n // after(function() {\n // return Promise.all([\n // \'archives/test*\',\n // \'test/.tmp\'\n // ].map(function(glob_pattern) {\n // return globAsync(glob_pattern)\n // .each(function(filename) {\n // // make every file writeable so the git packfiles can be removed\n // return fs.chmodAsync(filename, \'755\')\n // .then(function() { fs.removeAsync(filename); });\n // })\n // }));\n // });\n\n describe(\'#make\', function() {\n it(\'creates an archive\', function() {\n var modified_pkg = _.cloneDeep(pkg);\n modified_pkg.name = \'test_0000000001\';\n var archive_location = path.resolve(\'archives\', modified_pkg.name + \'.tar.gz\');\n var test_archive = new Archive(modified_pkg);\n\n return test_archive.make()\n .then(function() { return fs.statAsync(archive_location); })\n .then(function(file) { return expect(file).to.exist; })\n .catch(function(e) { return expect(e).to.not.exist; });\n });\n\n it(\'creates a gzip compressed archive\', function() {\n var modified_pkg = _.cloneDeep(pkg);\n modified_pkg.name = \'test_0000000002\';\n var archive_location = path.resolve(\'archives\', modified_pkg.name + \'.tar.gz\');\n var test_archive = new Archive(modified_pkg);\n\n // inspired from https://github.com/mafintosh/gunzip-maybe/blob/master/index.js#L6-L11\n var isGzipped = function(data) {\n var GZIP_MAGIC_BYTES = [0x1f, 0x8b];\n var DEFLATE_COMPRESSION_METHOD = 0x08;\n var buffer = data[1];\n\n if (buffer[0] !== GZIP_MAGIC_BYTES[0] && buffer[1] !== GZIP_MAGIC_BYTES[1]) return false;\n if (buffer[2] !== DEFLATE_COMPRESSION_METHOD) return false;\n return true;\n };\n\n return test_archive.make()\n .then(function() { return fs.openAsync(archive_location, \'r\'); })\n .then(function(fd) { \n var buffer = new Buffer(10);\n var buffer_offset = 0;\n var buffer_length = 10;\n var file_position = 0;\n return fs.readAsync(fd, buffer, buffer_offset, buffer_length, file_position);\n })\n .then(function(data) { console.log(\'data\', data); return data; })\n .then(function(data) { return expect(isGzipped(data)).to.be.true; })\n });\n\n it(\'has the correct directory structure\', function() {\n var modified_pkg = _.cloneDeep(pkg);\n modified_pkg.name = \'test_0000000003\';\n var archive_location = path.resolve(\'archives\', modified_pkg.name + \'.tar.gz\');\n var test_archive = new Archive(modified_pkg);\n var tmp_extract_path = path.resolve(\'test/.tmp\');\n\n var tarPromise = function(archive_path) {\n return new Promise(function(resolve, reject) {\n fs.createReadStream(archive_path)\n .pipe(zlib.Unzip())\n .pipe(tar.extract(tmp_extract_path))\n .on(\'error\', reject)\n .on(\'finish\', resolve);\n })\n };\n\n var verifyDir = function() {\n return Promise.all([\n \'code\',\n \'pdf\',\n \'code/coverall\',\n \'code/coverall_documents\',\n \'code/coverall_documents/coverletters\',\n \'code/coverall_documents/coverletters/test\',\n \'code/coverall_documents/coverletters/shared\',\n \'code/coverall_documents/resume\',\n \'code/coverall_documents/coverletters\'\n ].map(function(subpath) {\n return expect(fs.statAsync(path.resolve(tmp_extract_path, subpath)))\n .to.be.fulfilled;\n }))\n };\n\n return test_archive.make()\n .then(function() { return tarPromise(archive_location); })\n .then(function() { return verifyDir(); });\n });\n\n it(\'removes the temporary dir\', function() {\n var modified_pkg = _.cloneDeep(pkg);\n modified_pkg.name = \'test_0000000004\';\n var archive_location = path.resolve(\'archives\', modified_pkg.name + \'.tar.gz\');\n var test_archive = new Archive(modified_pkg);\n var tmp_dir = path.resolve(\'.tmp\');\n\n return test_archive.make()\n .then(function() { return expect(fs.statAsync(tmp_dir)).to.be.rejected; });\n });\n });\n});\nRun Code Online (Sandbox Code Playgroud)\n\n结果是:
\n\n$ mocha test\n\n\n Archive\n #make\n \xe2\x9c\x93 creates an archive (644ms)\n 1) creates a gzip compressed archive\n 2) has the correct directory structure\n 3) removes the temporary dir\n\n\n 1 passing (2s)\n 3 failing\n\n 1) Archive #make creates a gzip compressed archive:\n Uncaught Error: write after end\n at writeAfterEnd (_stream_writable.js:167:12)\n at Gzip.Writable.write (_stream_writable.js:214:5)\n at ondata (node_modules/tar-fs/node_modules/tar-stream/node_modules/readable-stream/lib/_stream_readable.js:574:20)\n at readableAddChunk (node_modules/tar-fs/node_modules/tar-stream/node_modules/readable-stream/lib/_stream_readable.js:198:16)\n at Readable.push (node_modules/tar-fs/node_modules/tar-stream/node_modules/readable-stream/lib/_stream_readable.js:162:10)\n at Pack._encode (node_modules/tar-fs/node_modules/tar-stream/pack.js:154:17)\n at Pack.entry (node_modules/tar-fs/node_modules/tar-stream/pack.js:100:10)\n at onstat (node_modules/tar-fs/index.js:108:19)\n at node_modules/tar-fs/index.js:40:9\n at FSReqWrap.oncomplete (fs.js:95:15)\n\n 2) Archive #make has the correct directory structure:\n AssertionError: expected false to be true\n at Context.<anonymous> (test/archive_spec.js:96:10)\n\n 3) Archive #make removes the temporary dir:\n Uncaught Error: write after end\n at writeAfterEnd (_stream_writable.js:167:12)\n at Gzip.Writable.write (_stream_writable.js:214:5)\n at ondata (node_modules/tar-fs/node_modules/tar-stream/node_modules/readable-stream/lib/_stream_readable.js:574:20)\n at readableAddChunk (node_modules/tar-fs/node_modules/tar-stream/node_modules/readable-stream/lib/_stream_readable.js:198:16)\n at Readable.push (node_modules/tar-fs/node_modules/tar-stream/node_modules/readable-stream/lib/_stream_readable.js:162:10)\n at Pack._encode (node_modules/tar-fs/node_modules/tar-stream/pack.js:154:17)\n at Pack.entry (node_modules/tar-fs/node_modules/tar-stream/pack.js:100:10)\n at onstat (node_modules/tar-fs/index.js:108:19)\n at node_modules/tar-fs/index.js:40:9\n at FSReqWrap.oncomplete (fs.js:95:15)\nRun Code Online (Sandbox Code Playgroud)\n\n我怀疑存在竞争条件,因此我注释掉了该after块,看看它是否会产生任何影响,但事实并非如此。
我不明白它的Uncaught Error: write after end含义,也不明白为什么堆栈跟踪不可用,即使我正在使用Promise.longStackTraces(). 是什么导致了这个错误?
我的测试看起来过于复杂,并且在实例化不同test_archive对象时多次重复代码。我该如何重构它们?
您尝试重复使用同一个gzip实例,但这是行不通的。这也解释了为什么第一次测试效果很好。
因此,将您的var gzip = zlib.createGzip();行移至函数内部的右侧Archive.prototype._writeArchive。
| 归档时间: |
|
| 查看次数: |
1222 次 |
| 最近记录: |