require("should"); const path = require("path"); const zlib = require("zlib"); const stream = require("stream"); const fs = require("fs-extra"); const proxyquire = require("proxyquire").noPreserveCache(); let fakeNow = new Date(2012, 8, 12, 10, 37, 11); const mockNow = () => fakeNow; const RollingFileWriteStream = proxyquire("../lib/RollingFileWriteStream", { "./now": mockNow }); let fakedFsDate = fakeNow; const mockFs = require("fs-extra"); const oldStatSync = mockFs.statSync; mockFs.statSync = fd => { const result = oldStatSync(fd); result.mtime = fakedFsDate; return result; }; function generateTestFile(fileName) { const dirName = path.join( __dirname, "tmp_" + Math.floor(Math.random() * new Date()) ); fileName = fileName || "ignored.log"; const fileNameObj = path.parse(fileName); return { dir: dirName, base: fileNameObj.base, name: fileNameObj.name, ext: fileNameObj.ext, path: path.join(dirName, fileName) }; } function resetTime() { fakeNow = new Date(2012, 8, 12, 10, 37, 11); fakedFsDate = fakeNow; } describe("RollingFileWriteStream", () => { beforeEach(() => { resetTime(); }); after(() => { fs.readdirSync(__dirname) .filter(f => f.startsWith("tmp_")) .forEach(f => fs.removeSync(path.join(__dirname, f))); }); describe("with no arguments", () => { it("should throw an error", () => { (() => new RollingFileWriteStream()).should.throw( /(the )?"?path"? (argument )?must be (a|of type) string\. received (type )?undefined/i ); }); }); describe("with invalid options", () => { after(done => { fs.remove("filename", done); }); it("should complain about a negative maxSize", () => { (() => { new RollingFileWriteStream("filename", { maxSize: -3 }); }).should.throw("options.maxSize (-3) should be > 0"); (() => { new RollingFileWriteStream("filename", { maxSize: 0 }); }).should.throw("options.maxSize (0) should be > 0"); }); it("should complain about a negative numToKeep", () => { (() => { new RollingFileWriteStream("filename", { numToKeep: -3 }); }).should.throw("options.numToKeep (-3) should be > 0"); (() => { new RollingFileWriteStream("filename", { numToKeep: 0 }); }).should.throw("options.numToKeep (0) should be > 0"); }); }); describe("with default arguments", () => { const fileObj = generateTestFile(); let s; before(() => { s = new RollingFileWriteStream(fileObj.path); }); after(() => { s.end(() => fs.removeSync(fileObj.dir)); }); it("should take a filename and options, return Writable", () => { s.should.be.an.instanceOf(stream.Writable); s.currentFileStream.path.should.eql(fileObj.path); s.currentFileStream.mode.should.eql(420); s.currentFileStream.flags.should.eql("a"); }); it("should apply default options", () => { s.options.maxSize.should.eql(Number.MAX_SAFE_INTEGER); s.options.encoding.should.eql("utf8"); s.options.mode.should.eql(420); s.options.flags.should.eql("a"); s.options.compress.should.eql(false); s.options.keepFileExt.should.eql(false); }); }); describe("with 5 maxSize, rotating daily", () => { const fileObj = generateTestFile("noExtension"); let s; before(async () => { fakeNow = new Date(2012, 8, 12, 10, 37, 11); s = new RollingFileWriteStream(fileObj.path, { pattern: "yyyy-MM-dd", maxSize: 5 }); const flows = Array.from(Array(38).keys()).map(i => () => { fakeNow = new Date(2012, 8, 12 + parseInt(i / 5, 10), 10, 37, 11); return new Promise(resolve => { s.write(i.toString(), "utf8", () => resolve()); }); }); for (let i = 0; i < flows.length; i += 1) { await flows[i](); } }); after(done => { s.end(() => { fs.removeSync(fileObj.dir); done(); }); }); it("should rotate using filename with no extension", () => { const files = fs.readdirSync(fileObj.dir); const expectedFileList = [ fileObj.base, //353637 fileObj.base + ".2012-09-12.1", // 01234 fileObj.base + ".2012-09-13.1", // 56789 fileObj.base + ".2012-09-14.2", // 101112 fileObj.base + ".2012-09-14.1", // 1314 fileObj.base + ".2012-09-15.2", // 151617 fileObj.base + ".2012-09-15.1", // 1819 fileObj.base + ".2012-09-16.2", // 202122 fileObj.base + ".2012-09-16.1", // 2324 fileObj.base + ".2012-09-17.2", // 252627 fileObj.base + ".2012-09-17.1", // 2829 fileObj.base + ".2012-09-18.2", // 303132 fileObj.base + ".2012-09-18.1" // 3334 ]; files.should.containDeep(expectedFileList); files.length.should.equal(expectedFileList.length); fs.readFileSync(path.format(fileObj)) .toString() .should.equal("353637"); fs.readFileSync( path.format( Object.assign({}, fileObj, { base: fileObj.base + ".2012-09-12.1" }) ) ) .toString() .should.equal("01234"); fs.readFileSync( path.format( Object.assign({}, fileObj, { base: fileObj.base + ".2012-09-13.1" }) ) ) .toString() .should.equal("56789"); fs.readFileSync( path.format( Object.assign({}, fileObj, { base: fileObj.base + ".2012-09-14.2" }) ) ) .toString() .should.equal("101112"); fs.readFileSync( path.format( Object.assign({}, fileObj, { base: fileObj.base + ".2012-09-14.1" }) ) ) .toString() .should.equal("1314"); fs.readFileSync( path.format( Object.assign({}, fileObj, { base: fileObj.base + ".2012-09-15.2" }) ) ) .toString() .should.equal("151617"); fs.readFileSync( path.format( Object.assign({}, fileObj, { base: fileObj.base + ".2012-09-15.1" }) ) ) .toString() .should.equal("1819"); fs.readFileSync( path.format( Object.assign({}, fileObj, { base: fileObj.base + ".2012-09-16.2" }) ) ) .toString() .should.equal("202122"); fs.readFileSync( path.format( Object.assign({}, fileObj, { base: fileObj.base + ".2012-09-16.1" }) ) ) .toString() .should.equal("2324"); fs.readFileSync( path.format( Object.assign({}, fileObj, { base: fileObj.base + ".2012-09-17.2" }) ) ) .toString() .should.equal("252627"); fs.readFileSync( path.format( Object.assign({}, fileObj, { base: fileObj.base + ".2012-09-17.1" }) ) ) .toString() .should.equal("2829"); fs.readFileSync( path.format( Object.assign({}, fileObj, { base: fileObj.base + ".2012-09-18.2" }) ) ) .toString() .should.equal("303132"); fs.readFileSync( path.format( Object.assign({}, fileObj, { base: fileObj.base + ".2012-09-18.1" }) ) ) .toString() .should.equal("3334"); }); }); describe("with default arguments and recreated in the same day", () => { const fileObj = generateTestFile(); let s; before(async () => { const flows = Array.from(Array(3).keys()).map(() => () => { s = new RollingFileWriteStream(fileObj.path); return new Promise(resolve => { s.end("abc", "utf8", () => resolve()); }); }); for (let i = 0; i < flows.length; i += 1) { await flows[i](); } }); after(() => { fs.removeSync(fileObj.dir); }); it("should have only 1 file", () => { const files = fs.readdirSync(fileObj.dir); const expectedFileList = [fileObj.base]; files.should.containDeep(expectedFileList); files.length.should.equal(expectedFileList.length); fs.readFileSync( path.format( Object.assign({}, fileObj, { base: fileObj.base }) ) ) .toString() .should.equal("abcabcabc"); }); }); describe("with 5 maxSize, using filename with extension", () => { const fileObj = generateTestFile("withExtension.log"); let s; before(async () => { fakeNow = new Date(2012, 8, 12, 10, 37, 11); s = new RollingFileWriteStream(fileObj.path, { pattern: "yyyy-MM-dd", maxSize: 5 }); const flows = Array.from(Array(38).keys()).map(i => () => { fakeNow = new Date(2012, 8, 12 + parseInt(i / 10, 10), 10, 37, 11); return new Promise(resolve => { s.write(i.toString(), "utf8", () => resolve()); }); }); for (let i = 0; i < flows.length; i += 1) { await flows[i](); } }); after(done => { s.end(() => { fs.removeSync(fileObj.dir); done(); }) }); it("should rotate files within the day, and when the day changes", () => { const files = fs.readdirSync(fileObj.dir); const expectedFileList = [ fileObj.base, //3637 fileObj.base + ".2012-09-12.2", //01234 fileObj.base + ".2012-09-12.1", //56789 fileObj.base + ".2012-09-13.4", //101112 fileObj.base + ".2012-09-13.3", //131415 fileObj.base + ".2012-09-13.2", //161718 fileObj.base + ".2012-09-13.1", //19 fileObj.base + ".2012-09-14.4", //202122 fileObj.base + ".2012-09-14.3", //232425 fileObj.base + ".2012-09-14.2", //262728 fileObj.base + ".2012-09-14.1", //29 fileObj.base + ".2012-09-15.2", //303132 fileObj.base + ".2012-09-15.1" //333435 ]; files.should.containDeep(expectedFileList); files.length.should.equal(expectedFileList.length); fs.readFileSync(path.format(fileObj)) .toString() .should.equal("3637"); fs.readFileSync( path.format( Object.assign({}, fileObj, { base: fileObj.base + ".2012-09-12.2" }) ) ) .toString() .should.equal("01234"); fs.readFileSync( path.format( Object.assign({}, fileObj, { base: fileObj.base + ".2012-09-12.1" }) ) ) .toString() .should.equal("56789"); fs.readFileSync( path.format( Object.assign({}, fileObj, { base: fileObj.base + ".2012-09-13.4" }) ) ) .toString() .should.equal("101112"); fs.readFileSync( path.format( Object.assign({}, fileObj, { base: fileObj.base + ".2012-09-13.3" }) ) ) .toString() .should.equal("131415"); fs.readFileSync( path.format( Object.assign({}, fileObj, { base: fileObj.base + ".2012-09-13.2" }) ) ) .toString() .should.equal("161718"); fs.readFileSync( path.format( Object.assign({}, fileObj, { base: fileObj.base + ".2012-09-13.1" }) ) ) .toString() .should.equal("19"); fs.readFileSync( path.format( Object.assign({}, fileObj, { base: fileObj.base + ".2012-09-14.4" }) ) ) .toString() .should.equal("202122"); fs.readFileSync( path.format( Object.assign({}, fileObj, { base: fileObj.base + ".2012-09-14.3" }) ) ) .toString() .should.equal("232425"); fs.readFileSync( path.format( Object.assign({}, fileObj, { base: fileObj.base + ".2012-09-14.2" }) ) ) .toString() .should.equal("262728"); fs.readFileSync( path.format( Object.assign({}, fileObj, { base: fileObj.base + ".2012-09-14.1" }) ) ) .toString() .should.equal("29"); fs.readFileSync( path.format( Object.assign({}, fileObj, { base: fileObj.base + ".2012-09-15.2" }) ) ) .toString() .should.equal("303132"); fs.readFileSync( path.format( Object.assign({}, fileObj, { base: fileObj.base + ".2012-09-15.1" }) ) ) .toString() .should.equal("333435"); }); }); describe("with 5 maxSize and 3 files limit", () => { const fileObj = generateTestFile(); let s; before(async () => { fakeNow = new Date(2012, 8, 12, 10, 37, 11); s = new RollingFileWriteStream(fileObj.path, { maxSize: 5, numToKeep: 3 }); const flows = Array.from(Array(38).keys()).map(i => () => { fakeNow = new Date(2012, 8, 12 + parseInt(i / 5), 10, 37, 11); return new Promise(resolve => { s.write(i.toString(), "utf8", () => resolve()); }); }); for (let i = 0; i < flows.length; i += 1) { await flows[i](); } }); after(done => { s.end(() => { fs.removeSync(fileObj.dir); done(); }); }); it("should rotate with at most 3 backup files not including the hot one", () => { const files = fs.readdirSync(fileObj.dir); const expectedFileList = [ fileObj.base, fileObj.base + ".1", fileObj.base + ".2", fileObj.base + ".3" ]; files.should.containDeep(expectedFileList); files.length.should.equal(expectedFileList.length); fs.readFileSync(path.format(fileObj)) .toString() .should.equal("37"); fs.readFileSync( path.format( Object.assign({}, fileObj, { base: fileObj.base + ".1" }) ) ) .toString() .should.equal("343536"); fs.readFileSync( path.format( Object.assign({}, fileObj, { base: fileObj.base + ".2" }) ) ) .toString() .should.equal("313233"); fs.readFileSync( path.format( Object.assign({}, fileObj, { base: fileObj.base + ".3" }) ) ) .toString() .should.equal("282930"); }); }); describe("with 5 maxSize and 3 files limit, rotating daily", () => { const fileObj = generateTestFile(); let s; before(async () => { fakeNow = new Date(2012, 8, 12, 10, 37, 11); s = new RollingFileWriteStream(fileObj.path, { maxSize: 5, pattern: "yyyy-MM-dd", numToKeep: 3 }); const flows = Array.from(Array(38).keys()).map(i => () => { fakeNow = new Date(2012, 8, 12 + parseInt(i / 10), 10, 37, 11); return new Promise(resolve => { s.write(i.toString(), "utf8", () => resolve()); }); }); for (let i = 0; i < flows.length; i += 1) { await flows[i](); } }); after(done => { s.end(() => { fs.removeSync(fileObj.dir); done(); }); }); it("should rotate with at most 3 backup files not including the hot one", () => { const files = fs.readdirSync(fileObj.dir); const expectedFileList = [ fileObj.base, //3637 fileObj.base + ".2012-09-14.1", //29 fileObj.base + ".2012-09-15.2", //303132 fileObj.base + ".2012-09-15.1" //333435 ]; files.should.containDeep(expectedFileList); files.length.should.equal(expectedFileList.length); fs.readFileSync(path.format(fileObj)) .toString() .should.equal("3637"); fs.readFileSync( path.format( Object.assign({}, fileObj, { base: fileObj.base + ".2012-09-15.1" }) ) ) .toString() .should.equal("333435"); fs.readFileSync( path.format( Object.assign({}, fileObj, { base: fileObj.base + ".2012-09-15.2" }) ) ) .toString() .should.equal("303132"); fs.readFileSync( path.format( Object.assign({}, fileObj, { base: fileObj.base + ".2012-09-14.1" }) ) ) .toString() .should.equal("29"); }); }); describe("with date pattern dd-MM-yyyy", () => { const fileObj = generateTestFile(); let s; before(async () => { fakeNow = new Date(2012, 8, 12, 10, 37, 11); s = new RollingFileWriteStream(fileObj.path, { maxSize: 5, pattern: "dd-MM-yyyy" }); const flows = Array.from(Array(8).keys()).map(i => () => { fakeNow = new Date(2012, 8, 12 + parseInt(i / 5, 10), 10, 37, 11); return new Promise(resolve => { s.write(i.toString(), "utf8", () => resolve()); }); }); for (let i = 0; i < flows.length; i += 1) { await flows[i](); } }); after(done => { s.end(() => { fs.remove(fileObj.dir, done); }); }); it("should rotate with date pattern dd-MM-yyyy in the file name", () => { const files = fs.readdirSync(fileObj.dir); const expectedFileList = [fileObj.base, fileObj.base + ".12-09-2012.1"]; files.should.containDeep(expectedFileList); files.length.should.equal(expectedFileList.length); fs.readFileSync(path.format(fileObj)) .toString() .should.equal("567"); fs.readFileSync( path.format( Object.assign({}, fileObj, { base: fileObj.base + ".12-09-2012.1" }) ) ) .toString() .should.equal("01234"); }); }); describe("with compress true", () => { const fileObj = generateTestFile(); let s; before(async () => { fakeNow = new Date(2012, 8, 12, 10, 37, 11); s = new RollingFileWriteStream(fileObj.path, { maxSize: 5, pattern: "yyyy-MM-dd", compress: true }); const flows = Array.from(Array(8).keys()).map(i => () => { fakeNow = new Date(2012, 8, 12 + parseInt(i / 5, 10), 10, 37, 11); return new Promise(resolve => { s.write(i.toString(), "utf8", () => resolve()); }); }); for (let i = 0; i < flows.length; i += 1) { await flows[i](); } }); after(done => { s.end(() => { fs.removeSync(fileObj.dir); done(); }); }); it("should rotate with gunzip", () => { const files = fs.readdirSync(fileObj.dir); const expectedFileList = [ fileObj.base, fileObj.base + ".2012-09-12.1.gz" ]; files.should.containDeep(expectedFileList); files.length.should.equal(expectedFileList.length); fs.readFileSync(path.format(fileObj)) .toString() .should.equal("567"); const content = fs.readFileSync( path.format( Object.assign({}, fileObj, { base: fileObj.base + ".2012-09-12.1.gz" }) ) ); zlib .gunzipSync(content) .toString() .should.equal("01234"); }); }); describe("with keepFileExt", () => { const fileObj = generateTestFile("keepFileExt.log"); let s; before(async () => { fakeNow = new Date(2012, 8, 12, 10, 37, 11); s = new RollingFileWriteStream(fileObj.path, { pattern: "yyyy-MM-dd", maxSize: 5, keepFileExt: true }); const flows = Array.from(Array(8).keys()).map(i => () => { fakeNow = new Date(2012, 8, 12 + parseInt(i / 5, 10), 10, 37, 11); return new Promise(resolve => { s.write(i.toString(), "utf8", () => resolve()); }); }); for (let i = 0; i < flows.length; i += 1) { await flows[i](); } }); after(done => { s.end(() => { fs.removeSync(fileObj.dir); done(); }); }); it("should rotate with the same extension", () => { const files = fs.readdirSync(fileObj.dir); const expectedFileList = [ fileObj.base, fileObj.name + ".2012-09-12.1.log" ]; files.should.containDeep(expectedFileList); files.length.should.equal(expectedFileList.length); fs.readFileSync(path.format(fileObj)) .toString() .should.equal("567"); fs.readFileSync( path.format({ dir: fileObj.dir, base: fileObj.name + ".2012-09-12.1" + fileObj.ext }) ) .toString() .should.equal("01234"); }); }); describe("with keepFileExt and compress", () => { const fileObj = generateTestFile("keepFileExt.log"); let s; before(async () => { fakeNow = new Date(2012, 8, 12, 10, 37, 11); s = new RollingFileWriteStream(fileObj.path, { maxSize: 5, pattern: "yyyy-MM-dd", keepFileExt: true, compress: true }); const flows = Array.from(Array(8).keys()).map(i => () => { fakeNow = new Date(2012, 8, 12 + parseInt(i / 5, 10), 10, 37, 11); return new Promise(resolve => { s.write(i.toString(), "utf8", () => resolve()); }); }); for (let i = 0; i < flows.length; i += 1) { await flows[i](); } }); after(done => { s.end(() => { fs.removeSync(fileObj.dir); done(); }); }); it("should rotate with the same extension", () => { const files = fs.readdirSync(fileObj.dir); const expectedFileList = [ fileObj.base, fileObj.name + ".2012-09-12.1.log.gz" ]; files.should.containDeep(expectedFileList); files.length.should.equal(expectedFileList.length); fs.readFileSync(path.format(fileObj)) .toString() .should.equal("567"); const content = fs.readFileSync( path.format( Object.assign({}, fileObj, { base: fileObj.name + ".2012-09-12.1.log.gz" }) ) ); zlib .gunzipSync(content) .toString() .should.equal("01234"); }); }); describe("with alwaysIncludePattern and keepFileExt", () => { const fileObj = generateTestFile("keepFileExt.log"); let s; before(async () => { fakeNow = new Date(2012, 8, 12, 10, 37, 11); s = new RollingFileWriteStream(fileObj.path, { maxSize: 5, pattern: "yyyy-MM-dd", keepFileExt: true, alwaysIncludePattern: true }); const flows = Array.from(Array(8).keys()).map(i => () => { fakeNow = new Date(2012, 8, 12 + parseInt(i / 5, 10), 10, 37, 11); return new Promise(resolve => { s.write(i.toString(), "utf8", () => resolve()); }); }); for (let i = 0; i < flows.length; i += 1) { await flows[i](); } }); after(done => { s.end(() => { fs.removeSync(fileObj.dir); done(); }); }); it("should rotate with the same extension and keep date in the filename", () => { const files = fs.readdirSync(fileObj.dir); const expectedFileList = [ fileObj.name + ".2012-09-12.1.log", fileObj.name + ".2012-09-13.log" ]; files.should.containDeep(expectedFileList); files.length.should.equal(expectedFileList.length); fs.readFileSync( path.format( Object.assign({}, fileObj, { base: fileObj.name + ".2012-09-13.log" }) ) ) .toString() .should.equal("567"); fs.readFileSync( path.format( Object.assign({}, fileObj, { base: fileObj.name + ".2012-09-12.1.log" }) ) ) .toString() .should.equal("01234"); }); }); describe("with 5 maxSize, compress, keepFileExt and alwaysIncludePattern", () => { const fileObj = generateTestFile("keepFileExt.log"); let s; before(async () => { fakeNow = new Date(2012, 8, 12, 10, 37, 11); s = new RollingFileWriteStream(fileObj.path, { maxSize: 5, compress: true, keepFileExt: true, alwaysIncludePattern: true, pattern: "yyyy-MM-dd" }); const flows = Array.from(Array(38).keys()).map(i => () => { fakeNow = new Date(2012, 8, 12 + parseInt(i / 5, 10), 10, 37, 11); return new Promise(resolve => { s.write(i.toString(), "utf8", () => resolve()); }); }); for (let i = 0; i < flows.length; i += 1) { await flows[i](); } }); after(done => { s.end(() => { fs.removeSync(fileObj.dir); done(); }); }); it("should rotate every day", () => { const files = fs.readdirSync(fileObj.dir); const expectedFileList = [ fileObj.name + ".2012-09-12.1.log.gz", //01234 fileObj.name + ".2012-09-13.1.log.gz", //56789 fileObj.name + ".2012-09-14.2.log.gz", //101112 fileObj.name + ".2012-09-14.1.log.gz", //1314 fileObj.name + ".2012-09-15.2.log.gz", //151617 fileObj.name + ".2012-09-15.1.log.gz", //1819 fileObj.name + ".2012-09-16.2.log.gz", //202122 fileObj.name + ".2012-09-16.1.log.gz", //2324 fileObj.name + ".2012-09-17.2.log.gz", //252627 fileObj.name + ".2012-09-17.1.log.gz", //2829 fileObj.name + ".2012-09-18.2.log.gz", //303132 fileObj.name + ".2012-09-18.1.log.gz", //3334 fileObj.name + ".2012-09-19.log" //353637 ]; files.should.containDeep(expectedFileList); files.length.should.equal(expectedFileList.length); fs.readFileSync( path.format( Object.assign({}, fileObj, { base: fileObj.name + ".2012-09-19.log" }) ) ) .toString() .should.equal("353637"); zlib .gunzipSync( fs.readFileSync( path.format( Object.assign({}, fileObj, { base: fileObj.name + ".2012-09-18.1.log.gz" }) ) ) ) .toString() .should.equal("3334"); zlib .gunzipSync( fs.readFileSync( path.format( Object.assign({}, fileObj, { base: fileObj.name + ".2012-09-18.2.log.gz" }) ) ) ) .toString() .should.equal("303132"); zlib .gunzipSync( fs.readFileSync( path.format( Object.assign({}, fileObj, { base: fileObj.name + ".2012-09-17.1.log.gz" }) ) ) ) .toString() .should.equal("2829"); zlib .gunzipSync( fs.readFileSync( path.format( Object.assign({}, fileObj, { base: fileObj.name + ".2012-09-17.2.log.gz" }) ) ) ) .toString() .should.equal("252627"); zlib .gunzipSync( fs.readFileSync( path.format( Object.assign({}, fileObj, { base: fileObj.name + ".2012-09-16.1.log.gz" }) ) ) ) .toString() .should.equal("2324"); zlib .gunzipSync( fs.readFileSync( path.format( Object.assign({}, fileObj, { base: fileObj.name + ".2012-09-16.2.log.gz" }) ) ) ) .toString() .should.equal("202122"); zlib .gunzipSync( fs.readFileSync( path.format( Object.assign({}, fileObj, { base: fileObj.name + ".2012-09-15.1.log.gz" }) ) ) ) .toString() .should.equal("1819"); zlib .gunzipSync( fs.readFileSync( path.format( Object.assign({}, fileObj, { base: fileObj.name + ".2012-09-15.2.log.gz" }) ) ) ) .toString() .should.equal("151617"); zlib .gunzipSync( fs.readFileSync( path.format( Object.assign({}, fileObj, { base: fileObj.name + ".2012-09-14.1.log.gz" }) ) ) ) .toString() .should.equal("1314"); zlib .gunzipSync( fs.readFileSync( path.format( Object.assign({}, fileObj, { base: fileObj.name + ".2012-09-14.2.log.gz" }) ) ) ) .toString() .should.equal("101112"); zlib .gunzipSync( fs.readFileSync( path.format( Object.assign({}, fileObj, { base: fileObj.name + ".2012-09-13.1.log.gz" }) ) ) ) .toString() .should.equal("56789"); zlib .gunzipSync( fs.readFileSync( path.format( Object.assign({}, fileObj, { base: fileObj.name + ".2012-09-12.1.log.gz" }) ) ) ) .toString() .should.equal("01234"); }); }); describe("when old files exist", () => { const fileObj = generateTestFile(); let s; before(done => { fakeNow = new Date(2012, 8, 12, 10, 37, 11); fs.ensureFileSync(fileObj.path); fs.writeFileSync(fileObj.path, "exist"); s = new RollingFileWriteStream(fileObj.path); s.write("now", "utf8", done); }); after(done => { s.end(() => { fs.removeSync(fileObj.dir); done(); }); }); it("should use write in the old file if not reach the maxSize limit", () => { const files = fs.readdirSync(fileObj.dir); const expectedFileList = [fileObj.base]; files.should.containDeep(expectedFileList); files.length.should.equal(expectedFileList.length); fs.readFileSync(path.format(fileObj)) .toString() .should.equal("existnow"); }); }); describe("when old files exist with contents", () => { const fileObj = generateTestFile(); let s; before(done => { fakeNow = new Date(2012, 8, 12, 10, 37, 11); fs.ensureFileSync(fileObj.path); fs.writeFileSync(fileObj.path, "This is exactly 30 bytes long\n"); s = new RollingFileWriteStream(fileObj.path, { maxSize: 35 }); s.write("one\n", "utf8"); //34 s.write("two\n", "utf8"); //38 - file should be rotated next time s.write("three\n", "utf8", done); // this should be in a new file. }); after(done => { s.end(() => { fs.removeSync(fileObj.dir); done(); }); }); it("should respect the existing file size", () => { const files = fs.readdirSync(fileObj.dir); const expectedFileList = [fileObj.base, fileObj.base + ".1"]; files.should.containDeep(expectedFileList); files.length.should.equal(expectedFileList.length); fs.readFileSync(path.format(fileObj)) .toString() .should.equal("three\n"); fs.readFileSync(path.join(fileObj.dir, fileObj.base + ".1")) .toString() .should.equal("This is exactly 30 bytes long\none\ntwo\n"); }); }); describe("when old files exist with contents and the flag is a+", () => { const fileObj = generateTestFile(); let s; before(done => { fakeNow = new Date(2012, 8, 12, 10, 37, 11); fs.ensureFileSync(fileObj.path); fs.writeFileSync(fileObj.path, "This is exactly 30 bytes long\n"); s = new RollingFileWriteStream(fileObj.path, { maxSize: 35, flags: "a+" }); s.write("one\n", "utf8"); //34 s.write("two\n", "utf8"); //38 - file should be rotated next time s.write("three\n", "utf8", done); // this should be in a new file. }); after(done => { s.end(() => { fs.removeSync(fileObj.dir); done(); }); }); it("should respect the existing file size", () => { const files = fs.readdirSync(fileObj.dir); const expectedFileList = [fileObj.base, fileObj.base + ".1"]; files.should.containDeep(expectedFileList); files.length.should.equal(expectedFileList.length); fs.readFileSync(path.format(fileObj)) .toString() .should.equal("three\n"); fs.readFileSync(path.join(fileObj.dir, fileObj.base + ".1")) .toString() .should.equal("This is exactly 30 bytes long\none\ntwo\n"); }); }); describe("when old files exist with indices", () => { const fileObj = generateTestFile(); let s; before(done => { fs.ensureFileSync(fileObj.path); fs.writeFileSync( fileObj.path, "This was the base file and it should be more than 30 bytes\n" ); // base fs.writeFileSync(fileObj.path + ".1", "This was the first old file\n"); // base.1 s = new RollingFileWriteStream(fileObj.path, { maxSize: 30, numToKeep: 5 }); s.write("This is exactly 30 bytes long\n", "utf8"); // base.1 -> base.2, base -> base.1 s.write("This is exactly 30 bytes long\n", "utf8"); // base.2 -> base.3, base.1 -> base.2, base -> base.1 s.write("three\n", "utf8", done); // base.3 -> base.4, base.2 -> base.3, base.1 -> base.2, base -> base.1 }); after(done => { s.end(() => { fs.removeSync(fileObj.dir); done(); }); }); it("should rotate the old file indices", () => { const files = fs.readdirSync(fileObj.dir); const expectedFileList = [ fileObj.base, fileObj.base + ".1", fileObj.base + ".2", fileObj.base + ".3", fileObj.base + ".4" ]; files.should.containDeep(expectedFileList); files.length.should.equal(expectedFileList.length); fs.readFileSync(path.format(fileObj)) .toString() .should.equal("three\n"); fs.readFileSync(path.join(fileObj.dir, fileObj.base + ".1")) .toString() .should.equal("This is exactly 30 bytes long\n"); fs.readFileSync(path.join(fileObj.dir, fileObj.base + ".2")) .toString() .should.equal("This is exactly 30 bytes long\n"); fs.readFileSync(path.join(fileObj.dir, fileObj.base + ".3")) .toString() .should.equal( "This was the base file and it should be more than 30 bytes\n" ); fs.readFileSync(path.join(fileObj.dir, fileObj.base + ".4")) .toString() .should.equal("This was the first old file\n"); }); }); describe("when old files exist with contents and rolling by date", () => { const fileObj = generateTestFile(); let s; before(done => { fakeNow = new Date(2012, 8, 12, 10, 37, 11); fs.ensureFileSync(fileObj.path); fs.writeFileSync(fileObj.path, "This was created Sept 12, 2012.\n"); fakeNow = new Date(2012, 8, 13, 10, 53, 12); s = new RollingFileWriteStream(fileObj.path, { pattern: "yyyy-MM-dd" }); s.write("It is now Sept 13, 2012.\n", "utf8", done); // this should be in a new file. }); after(done => { s.end(() => { fs.removeSync(fileObj.dir); done(); }); }); it("should respect the existing file date", () => { const files = fs.readdirSync(fileObj.dir); const expectedFileList = [fileObj.base, fileObj.base + ".2012-09-12"]; files.should.containDeep(expectedFileList); files.length.should.equal(expectedFileList.length); fs.readFileSync(path.format(fileObj)) .toString() .should.equal("It is now Sept 13, 2012.\n"); fs.readFileSync(path.join(fileObj.dir, fileObj.base + ".2012-09-12")) .toString() .should.equal("This was created Sept 12, 2012.\n"); }); }); describe("when old files exist with contents and stream created with overwrite flag", () => { const fileObj = generateTestFile(); let s; before(done => { fakeNow = new Date(2012, 8, 12, 10, 37, 11); fs.ensureFileSync(fileObj.path); fs.writeFileSync(fileObj.path, "This is exactly 30 bytes long\n"); s = new RollingFileWriteStream(fileObj.path, { maxSize: 35, flags: "w" }); s.write("there should only be this\n", "utf8", done); }); after(done => { s.end(() => { fs.removeSync(fileObj.dir); done(); }); }); it("should ignore the existing file size", () => { const files = fs.readdirSync(fileObj.dir); const expectedFileList = [fileObj.base]; files.should.containDeep(expectedFileList); files.length.should.equal(expectedFileList.length); s.state.currentSize.should.equal(26); fs.readFileSync(path.format(fileObj)) .toString() .should.equal("there should only be this\n"); }); }); describe("when dir does not exist", () => { const fileObj = generateTestFile(); let s; before(done => { fs.removeSync(fileObj.dir); fakeNow = new Date(2012, 8, 12, 10, 37, 11); s = new RollingFileWriteStream(fileObj.path); s.write("test", "utf8", done); }); after(done => { s.end(() => { fs.removeSync(fileObj.dir); done(); }); }); it("should create the dir", () => { const files = fs.readdirSync(fileObj.dir); const expectedFileList = [fileObj.base]; files.should.containDeep(expectedFileList); files.length.should.equal(expectedFileList.length); fs.readFileSync(path.format(fileObj)) .toString() .should.equal("test"); }); }); describe("when given just a base filename with no dir", () => { let s; before(done => { s = new RollingFileWriteStream("test.log"); s.write("this should not cause any problems", "utf8", done); }); after(done => { s.end(() => { fs.removeSync("test.log"); done(); }); }); it("should use process.cwd() as the dir", () => { const files = fs.readdirSync(process.cwd()); files.should.containDeep(["test.log"]); fs.readFileSync(path.join(process.cwd(), "test.log")) .toString() .should.equal("this should not cause any problems"); }); }); describe("with no callback to write", () => { let s; before(done => { s = new RollingFileWriteStream("no-callback.log"); s.write("this is all very nice", "utf8", done); }); after(done => { fs.remove("no-callback.log", done); }); it("should not complain", done => { s.write("I am not bothered if this succeeds or not"); s.end(done); }); }); describe("events", () => { let s; before(done => { s = new RollingFileWriteStream("test-events.log"); s.write("this should not cause any problems", "utf8", done); }); after(done => { s.end(() => { fs.removeSync("test-events.log"); done(); }); }); it("should emit the error event of the underlying stream", done => { s.on("error", e => { e.message.should.equal("oh no"); done(); }); s.currentFileStream.emit("error", new Error("oh no")); }); }); describe("when deleting old files and there is an error", () => { before(done => { fs.ensureDir('/tmp/delete-test/logfile.log.2', done); }); it("should not let errors bubble up", done => { const s = new RollingFileWriteStream("/tmp/delete-test/logfile.log", { maxSize: 10, numToKeep: 1 }); s.write("length is 10", "utf8", () => { // if there's an error during deletion, then done never gets called s.write("length is 10", "utf8", done); }); }); after(done => { fs.remove('/tmp/delete-test', done); }) }); });