RollingFileStream-test.js 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559
  1. "use strict";
  2. require("should");
  3. var async = require("async"),
  4. fs = require("fs"),
  5. path = require("path"),
  6. zlib = require("zlib"),
  7. streams = require("stream"),
  8. RollingFileStream = require("../lib").RollingFileStream;
  9. function remove(filename, cb) {
  10. fs.unlink(filename, function() {
  11. cb();
  12. });
  13. }
  14. function create(filename, cb) {
  15. fs.writeFile(filename, "test file", cb);
  16. }
  17. describe("RollingFileStream", function() {
  18. describe("arguments", function() {
  19. var stream;
  20. before(function(done) {
  21. remove(__dirname + "/test-rolling-file-stream", function() {
  22. stream = new RollingFileStream(
  23. __dirname + "/test-rolling-file-stream",
  24. 1024,
  25. 5
  26. );
  27. done();
  28. });
  29. });
  30. after(function(done) {
  31. remove(__dirname + "/test-rolling-file-stream", done);
  32. });
  33. it("should take a filename, file size (bytes), no. backups, return Writable", function() {
  34. stream.should.be.an.instanceOf(streams.Writable);
  35. stream.filename.should.eql(__dirname + "/test-rolling-file-stream");
  36. stream.size.should.eql(1024);
  37. stream.backups.should.eql(5);
  38. });
  39. it("should apply default settings to the underlying stream", function() {
  40. stream.theStream.mode.should.eql(420);
  41. stream.theStream.flags.should.eql("a");
  42. });
  43. });
  44. describe("with stream arguments", function() {
  45. it("should pass them to the underlying stream", function() {
  46. var stream = new RollingFileStream(
  47. __dirname + "/test-rolling-file-stream",
  48. 1024,
  49. 5,
  50. { mode: parseInt("0666", 8) }
  51. );
  52. stream.theStream.mode.should.eql(parseInt("0666", 8));
  53. });
  54. after(function(done) {
  55. remove(__dirname + "/test-rolling-file-stream", done);
  56. });
  57. });
  58. describe("without size", function() {
  59. it("should default to max int size", function() {
  60. var stream = new RollingFileStream(
  61. __dirname + "/test-rolling-file-stream"
  62. );
  63. stream.size.should.eql(Number.MAX_SAFE_INTEGER);
  64. });
  65. after(function(done) {
  66. remove(__dirname + "/test-rolling-file-stream", done);
  67. });
  68. });
  69. describe("without number of backups", function() {
  70. it("should default to 1 backup", function() {
  71. var stream = new RollingFileStream(
  72. __dirname + "/test-rolling-file-stream",
  73. 1024
  74. );
  75. stream.backups.should.eql(1);
  76. });
  77. after(function(done) {
  78. remove(__dirname + "/test-rolling-file-stream", done);
  79. });
  80. });
  81. describe("writing less than the file size", function() {
  82. before(function(done) {
  83. remove(__dirname + "/test-rolling-file-stream-write-less", function() {
  84. var stream = new RollingFileStream(
  85. __dirname + "/test-rolling-file-stream-write-less",
  86. 100
  87. );
  88. stream.write("cheese", "utf8", function() {
  89. stream.end(done);
  90. });
  91. });
  92. });
  93. after(function(done) {
  94. remove(__dirname + "/test-rolling-file-stream-write-less", done);
  95. });
  96. it("should write to the file", function(done) {
  97. fs.readFile(
  98. __dirname + "/test-rolling-file-stream-write-less",
  99. "utf8",
  100. function(err, contents) {
  101. contents.should.eql("cheese");
  102. done(err);
  103. }
  104. );
  105. });
  106. it("should write one file", function(done) {
  107. fs.readdir(__dirname, function(err, files) {
  108. files
  109. .filter(function(file) {
  110. return file.indexOf("test-rolling-file-stream-write-less") > -1;
  111. })
  112. .should.have.length(1);
  113. done(err);
  114. });
  115. });
  116. });
  117. describe("writing more than the file size", function() {
  118. before(function(done) {
  119. async.forEach(
  120. [
  121. __dirname + "/test-rolling-file-stream-write-more",
  122. __dirname + "/test-rolling-file-stream-write-more.1"
  123. ],
  124. remove,
  125. function() {
  126. var stream = new RollingFileStream(
  127. __dirname + "/test-rolling-file-stream-write-more",
  128. 45
  129. );
  130. async.forEachSeries(
  131. [0, 1, 2, 3, 4, 5, 6],
  132. function(i, cb) {
  133. stream.write(i + ".cheese\n", "utf8", cb);
  134. },
  135. function() {
  136. stream.end(done);
  137. }
  138. );
  139. }
  140. );
  141. });
  142. after(function(done) {
  143. async.forEach(
  144. [
  145. __dirname + "/test-rolling-file-stream-write-more",
  146. __dirname + "/test-rolling-file-stream-write-more.1"
  147. ],
  148. remove,
  149. done
  150. );
  151. });
  152. it("should write two files", function(done) {
  153. fs.readdir(__dirname, function(err, files) {
  154. files
  155. .filter(function(file) {
  156. return file.indexOf("test-rolling-file-stream-write-more") > -1;
  157. })
  158. .should.have.length(2);
  159. done(err);
  160. });
  161. });
  162. it("should write the last two log messages to the first file", function(done) {
  163. fs.readFile(
  164. __dirname + "/test-rolling-file-stream-write-more",
  165. "utf8",
  166. function(err, contents) {
  167. contents.should.eql("5.cheese\n6.cheese\n");
  168. done(err);
  169. }
  170. );
  171. });
  172. it("should write the first five log messages to the second file", function(done) {
  173. fs.readFile(
  174. __dirname + "/test-rolling-file-stream-write-more.1",
  175. "utf8",
  176. function(err, contents) {
  177. contents.should.eql(
  178. "0.cheese\n1.cheese\n2.cheese\n3.cheese\n4.cheese\n"
  179. );
  180. done(err);
  181. }
  182. );
  183. });
  184. });
  185. describe("with options.compress = true", function() {
  186. before(function(done) {
  187. var stream = new RollingFileStream(
  188. path.join(__dirname, "compressed-backups.log"),
  189. 30, //30 bytes max size
  190. 2, //two backup files to keep
  191. { compress: true }
  192. );
  193. async.forEachSeries(
  194. [
  195. "This is the first log message.",
  196. "This is the second log message.",
  197. "This is the third log message.",
  198. "This is the fourth log message."
  199. ],
  200. function(i, cb) {
  201. stream.write(i + "\n", "utf8", cb);
  202. },
  203. function() {
  204. stream.end(done);
  205. }
  206. );
  207. });
  208. it("should produce three files, with the backups compressed", function(done) {
  209. fs.readdir(__dirname, function(err, files) {
  210. var testFiles = files
  211. .filter(function(f) {
  212. return f.indexOf("compressed-backups.log") > -1;
  213. })
  214. .sort();
  215. testFiles.length.should.eql(3);
  216. testFiles.should.eql([
  217. "compressed-backups.log",
  218. "compressed-backups.log.1.gz",
  219. "compressed-backups.log.2.gz"
  220. ]);
  221. fs.readFile(path.join(__dirname, testFiles[0]), "utf8", function(
  222. err,
  223. contents
  224. ) {
  225. contents.should.eql("This is the fourth log message.\n");
  226. zlib.gunzip(
  227. fs.readFileSync(path.join(__dirname, testFiles[1])),
  228. function(err, contents) {
  229. contents
  230. .toString("utf8")
  231. .should.eql("This is the third log message.\n");
  232. zlib.gunzip(
  233. fs.readFileSync(path.join(__dirname, testFiles[2])),
  234. function(err, contents) {
  235. contents
  236. .toString("utf8")
  237. .should.eql("This is the second log message.\n");
  238. done(err);
  239. }
  240. );
  241. }
  242. );
  243. });
  244. });
  245. });
  246. after(function(done) {
  247. async.forEach(
  248. [
  249. path.join(__dirname, "compressed-backups.log"),
  250. path.join(__dirname, "compressed-backups.log.1.gz"),
  251. path.join(__dirname, "compressed-backups.log.2.gz")
  252. ],
  253. remove,
  254. done
  255. );
  256. });
  257. });
  258. describe("with options.keepFileExt = true", function() {
  259. before(function(done) {
  260. var stream = new RollingFileStream(
  261. path.join(__dirname, "extKept-backups.log"),
  262. 30, //30 bytes max size
  263. 2, //two backup files to keep
  264. { keepFileExt: true }
  265. );
  266. async.forEachSeries(
  267. [
  268. "This is the first log message.",
  269. "This is the second log message.",
  270. "This is the third log message.",
  271. "This is the fourth log message."
  272. ],
  273. function(i, cb) {
  274. stream.write(i + "\n", "utf8", cb);
  275. },
  276. function() {
  277. stream.end(done);
  278. }
  279. );
  280. });
  281. it("should produce three files, with the file-extension kept", function(done) {
  282. fs.readdir(__dirname, function(err, files) {
  283. var testFiles = files
  284. .filter(function(f) {
  285. return f.indexOf("extKept-backups") > -1;
  286. })
  287. .sort();
  288. testFiles.length.should.eql(3);
  289. testFiles.should.eql([
  290. "extKept-backups.1.log",
  291. "extKept-backups.2.log",
  292. "extKept-backups.log"
  293. ]);
  294. fs.readFile(path.join(__dirname, testFiles[0]), "utf8", function(
  295. err,
  296. contents
  297. ) {
  298. contents.should.eql("This is the third log message.\n");
  299. fs.readFile(path.join(__dirname, testFiles[1]), "utf8", function(
  300. err,
  301. contents
  302. ) {
  303. contents
  304. .toString("utf8")
  305. .should.eql("This is the second log message.\n");
  306. fs.readFile(path.join(__dirname, testFiles[2]), "utf8", function(
  307. err,
  308. contents
  309. ) {
  310. contents
  311. .toString("utf8")
  312. .should.eql("This is the fourth log message.\n");
  313. done(err);
  314. });
  315. });
  316. });
  317. });
  318. });
  319. after(function(done) {
  320. async.forEach(
  321. [
  322. path.join(__dirname, "extKept-backups.log"),
  323. path.join(__dirname, "extKept-backups.1.log"),
  324. path.join(__dirname, "extKept-backups.2.log")
  325. ],
  326. remove,
  327. done
  328. );
  329. });
  330. });
  331. describe("with options.compress = true and keepFileExt = true", function() {
  332. before(function(done) {
  333. var stream = new RollingFileStream(
  334. path.join(__dirname, "compressed-backups.log"),
  335. 30, //30 bytes max size
  336. 2, //two backup files to keep
  337. { compress: true, keepFileExt: true }
  338. );
  339. async.forEachSeries(
  340. [
  341. "This is the first log message.",
  342. "This is the second log message.",
  343. "This is the third log message.",
  344. "This is the fourth log message."
  345. ],
  346. function(i, cb) {
  347. stream.write(i + "\n", "utf8", cb);
  348. },
  349. function() {
  350. stream.end(done);
  351. }
  352. );
  353. });
  354. it("should produce three files, with the backups compressed", function(done) {
  355. fs.readdir(__dirname, function(err, files) {
  356. var testFiles = files
  357. .filter(function(f) {
  358. return f.indexOf("compressed-backups") > -1;
  359. })
  360. .sort();
  361. testFiles.length.should.eql(3);
  362. testFiles.should.eql([
  363. "compressed-backups.1.log.gz",
  364. "compressed-backups.2.log.gz",
  365. "compressed-backups.log"
  366. ]);
  367. fs.readFile(path.join(__dirname, testFiles[2]), "utf8", function(
  368. err,
  369. contents
  370. ) {
  371. contents.should.eql("This is the fourth log message.\n");
  372. zlib.gunzip(
  373. fs.readFileSync(path.join(__dirname, testFiles[1])),
  374. function(err, contents) {
  375. contents
  376. .toString("utf8")
  377. .should.eql("This is the second log message.\n");
  378. zlib.gunzip(
  379. fs.readFileSync(path.join(__dirname, testFiles[0])),
  380. function(err, contents) {
  381. contents
  382. .toString("utf8")
  383. .should.eql("This is the third log message.\n");
  384. done(err);
  385. }
  386. );
  387. }
  388. );
  389. });
  390. });
  391. });
  392. after(function(done) {
  393. async.forEach(
  394. [
  395. path.join(__dirname, "compressed-backups.log"),
  396. path.join(__dirname, "compressed-backups.1.log.gz"),
  397. path.join(__dirname, "compressed-backups.2.log.gz")
  398. ],
  399. remove,
  400. done
  401. );
  402. });
  403. });
  404. describe("when many files already exist", function() {
  405. before(function(done) {
  406. async.forEach(
  407. [
  408. __dirname + "/test-rolling-stream-with-existing-files.11",
  409. __dirname + "/test-rolling-stream-with-existing-files.20",
  410. __dirname + "/test-rolling-stream-with-existing-files.-1",
  411. __dirname + "/test-rolling-stream-with-existing-files.1.1",
  412. __dirname + "/test-rolling-stream-with-existing-files.1"
  413. ],
  414. remove,
  415. function(err) {
  416. if (err) done(err);
  417. async.forEach(
  418. [
  419. __dirname + "/test-rolling-stream-with-existing-files.11",
  420. __dirname + "/test-rolling-stream-with-existing-files.20",
  421. __dirname + "/test-rolling-stream-with-existing-files.-1",
  422. __dirname + "/test-rolling-stream-with-existing-files.1.1",
  423. __dirname + "/test-rolling-stream-with-existing-files.1"
  424. ],
  425. create,
  426. function(err) {
  427. if (err) done(err);
  428. var stream = new RollingFileStream(
  429. __dirname + "/test-rolling-stream-with-existing-files",
  430. 18,
  431. 5
  432. );
  433. async.forEachSeries(
  434. [0, 1, 2, 3, 4, 5, 6],
  435. function(i, cb) {
  436. stream.write(i + ".cheese\n", "utf8", cb);
  437. },
  438. function() {
  439. stream.end(done);
  440. }
  441. );
  442. }
  443. );
  444. }
  445. );
  446. });
  447. after(function(done) {
  448. async.forEach(
  449. [
  450. __dirname + "/test-rolling-stream-with-existing-files.-1",
  451. __dirname + "/test-rolling-stream-with-existing-files",
  452. __dirname + "/test-rolling-stream-with-existing-files.1.1",
  453. __dirname + "/test-rolling-stream-with-existing-files.0",
  454. __dirname + "/test-rolling-stream-with-existing-files.1",
  455. __dirname + "/test-rolling-stream-with-existing-files.2",
  456. __dirname + "/test-rolling-stream-with-existing-files.3",
  457. __dirname + "/test-rolling-stream-with-existing-files.4",
  458. __dirname + "/test-rolling-stream-with-existing-files.5",
  459. __dirname + "/test-rolling-stream-with-existing-files.6",
  460. __dirname + "/test-rolling-stream-with-existing-files.11",
  461. __dirname + "/test-rolling-stream-with-existing-files.20"
  462. ],
  463. remove,
  464. done
  465. );
  466. });
  467. it("should roll the files, removing the highest indices", function(done) {
  468. fs.readdir(__dirname, function(err, files) {
  469. files.should.containEql("test-rolling-stream-with-existing-files");
  470. files.should.containEql("test-rolling-stream-with-existing-files.1");
  471. files.should.containEql("test-rolling-stream-with-existing-files.2");
  472. files.should.containEql("test-rolling-stream-with-existing-files.3");
  473. files.should.containEql("test-rolling-stream-with-existing-files.4");
  474. done(err);
  475. });
  476. });
  477. });
  478. describe("when the directory gets deleted", function() {
  479. var stream;
  480. before(function(done) {
  481. stream = new RollingFileStream(
  482. path.join("subdir", "test-rolling-file-stream"),
  483. 5,
  484. 5
  485. );
  486. stream.write("initial", "utf8", done);
  487. });
  488. after(function() {
  489. fs.unlinkSync(path.join("subdir", "test-rolling-file-stream"));
  490. fs.rmdirSync("subdir");
  491. });
  492. it("handles directory deletion gracefully", function(done) {
  493. stream.theStream.on("error", done);
  494. remove(path.join("subdir", "test-rolling-file-stream"), function() {
  495. fs.rmdir("subdir", function() {
  496. stream.write("rollover", "utf8", function() {
  497. fs.readFileSync(
  498. path.join("subdir", "test-rolling-file-stream"),
  499. "utf8"
  500. ).should.eql("rollover");
  501. done();
  502. });
  503. });
  504. });
  505. });
  506. });
  507. });