├── .gitignore ├── .travis.yml ├── .npmignore ├── .editorconfig ├── .jshintrc ├── test ├── end.js ├── any.js ├── last.js ├── all.js ├── length.js ├── stream.js ├── nth.js └── piping.js ├── assertStream.js ├── package.json ├── readme.md └── index.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | coverage 3 | npm-debug.log 4 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - '0.10' 4 | - '0.12' 5 | - 'iojs' 6 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | img 2 | test 3 | .travis.yml 4 | .jshintrc 5 | .editorconfig 6 | coverage 7 | npm-debug.log 8 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # editorconfig.org 2 | root = true 3 | 4 | [*] 5 | indent_style = tab 6 | end_of_line = lf 7 | charset = utf-8 8 | trim_trailing_whitespace = true 9 | insert_final_newline = true 10 | 11 | [package.json] 12 | indent_style = space 13 | indent_size = 2 14 | 15 | [*.md] 16 | trim_trailing_whitespace = false 17 | -------------------------------------------------------------------------------- /.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "node": true, 3 | "esnext": true, 4 | "bitwise": true, 5 | "camelcase": true, 6 | "curly": true, 7 | "eqeqeq": true, 8 | "immed": true, 9 | "indent": 4, 10 | "newcap": true, 11 | "noarg": true, 12 | "quotmark": "single", 13 | "regexp": true, 14 | "undef": true, 15 | "unused": true, 16 | "strict": false, 17 | "trailing": true, 18 | "smarttabs": true 19 | } 20 | -------------------------------------------------------------------------------- /test/end.js: -------------------------------------------------------------------------------- 1 | /* global describe, it */ 2 | 3 | var intoStream = require('into-stream').obj; 4 | var assert = require('../'); 5 | var should = require('should'); 6 | 7 | describe('assert.end', function () { 8 | it('should help dump long streams', function (done) { 9 | var long = []; 10 | for (var i = 0; i < 100; i++) { long.push(i); } 11 | intoStream(long) 12 | .pipe(assert.end(done)); 13 | }); 14 | 15 | it('should invoke callback with error', function (done) { 16 | var long = []; 17 | for (var i = 0; i < 100; i++) { long.push(i); } 18 | intoStream(long) 19 | .pipe(assert.length(1)) 20 | .pipe(assert.end(function (err) { 21 | should.exist(err); 22 | done(); 23 | })); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /test/any.js: -------------------------------------------------------------------------------- 1 | /* global describe, it */ 2 | 3 | var intoStream = require('into-stream').obj; 4 | var assert = require('../'); 5 | var should = require('should'); 6 | var is = require('funsert'); 7 | 8 | describe('assert.any', function () { 9 | it('should find matching element in stream', function (done) { 10 | intoStream([1, 2]) 11 | .pipe(assert.any(is.equal(2))) 12 | .pipe(assert.end(done)); 13 | }); 14 | 15 | it('should emit end with error on wrong assertion', function (done) { 16 | intoStream([1]) 17 | .pipe(assert.any(is.equal(2))) 18 | .pipe(assert.end(function (err) { 19 | should.exist(err); 20 | err.message.should.eql('Nothing passing assertion'); 21 | done(); 22 | })); 23 | }); 24 | }); 25 | -------------------------------------------------------------------------------- /test/last.js: -------------------------------------------------------------------------------- 1 | /* global describe, it */ 2 | 3 | var intoStream = require('into-stream').obj; 4 | var assert = require('../'); 5 | var should = require('should'); 6 | var is = require('funsert'); 7 | 8 | describe('assert.last', function () { 9 | it('should check last object', function (done) { 10 | intoStream([1, 2]) 11 | .pipe(assert.last(is.equal(2))) 12 | .pipe(assert.end(done)); 13 | }); 14 | 15 | it('should emit end with error on wrong assertion', function (done) { 16 | intoStream([1]) 17 | .pipe(assert.last(is.equal(2))) 18 | .pipe(assert.end(function (err) { 19 | should.exist(err); 20 | err.message.should.eql('Last element is not passing assertion: 1 is not equal 2'); 21 | done(); 22 | })); 23 | }); 24 | }); 25 | -------------------------------------------------------------------------------- /test/all.js: -------------------------------------------------------------------------------- 1 | /* global describe, it */ 2 | 3 | var intoStream = require('into-stream').obj; 4 | var assert = require('../'); 5 | var should = require('should'); 6 | var is = require('funsert'); 7 | 8 | describe('assert.all', function () { 9 | it('should check all elements in stream', function (done) { 10 | intoStream([1, 1]) 11 | .pipe(assert.all(is.equal(1))) 12 | .pipe(assert.end(done)); 13 | }); 14 | 15 | it('should emit end with error on wrong assertion', function (done) { 16 | intoStream([1, 2]) 17 | .pipe(assert.all(is.equal(1))) 18 | .pipe(assert.end(function (err) { 19 | should.exist(err); 20 | err.message.should.eql('Element on 1 position is not passing assertion: 2 is not equal 1'); 21 | done(); 22 | })); 23 | }); 24 | }); 25 | -------------------------------------------------------------------------------- /assertStream.js: -------------------------------------------------------------------------------- 1 | var through = require('through2'); 2 | 3 | function assertStream(options, transform, flush) { 4 | options = options || {}; 5 | 6 | if (typeof options === 'function') { 7 | flush = transform; 8 | transform = options; 9 | options = {}; 10 | } 11 | 12 | options.highWatermark = options.highWatermark || 16; 13 | options.objectMode = options.objectMode || true; 14 | 15 | var stream = through(options, transform, flush); 16 | 17 | stream.on('pipe', function (source) { 18 | source.on('assertion', function (err) { 19 | stream.emit('assertion', err); 20 | }); 21 | }); 22 | 23 | stream.assertion = function (message) { 24 | this.emit('assertion', new Error(message)); 25 | }; 26 | 27 | return stream; 28 | } 29 | 30 | module.exports = assertStream; 31 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "stream-assert", 3 | "version": "2.0.3", 4 | "description": "Assertion library for streams", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "mocha -R spec" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "git://github.com/floatdrop/stream-assert.git" 12 | }, 13 | "keywords": [ 14 | "Assert", 15 | "stream" 16 | ], 17 | "author": "Vsevolod Strukchinsky ", 18 | "license": "MIT", 19 | "bugs": { 20 | "url": "https://github.com/floatdrop/stream-assert/issues" 21 | }, 22 | "homepage": "https://github.com/floatdrop/stream-assert", 23 | "devDependencies": { 24 | "funsert": "^0.1.0", 25 | "into-stream": "^2.0.0", 26 | "mocha": "^1.21.4", 27 | "should": "^4.0.4" 28 | }, 29 | "dependencies": { 30 | "through2": "^0.6.1" 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /test/length.js: -------------------------------------------------------------------------------- 1 | /* global describe, it */ 2 | 3 | var intoStream = require('into-stream').obj; 4 | var assert = require('../'); 5 | var should = require('should'); 6 | var is = require('funsert'); 7 | 8 | describe('assert.length', function () { 9 | it('should validate stream length', function (done) { 10 | intoStream([1]) 11 | .pipe(assert.length(1)) 12 | .pipe(assert.end(done)); 13 | }); 14 | 15 | it('should emit end with error on wrong assertion', function (done) { 16 | intoStream([1]) 17 | .pipe(assert.length(2)) 18 | .pipe(assert.end(function (err) { 19 | should.exist(err); 20 | err.message.should.eql('Expected length 2 is not equal 1'); 21 | done(); 22 | })); 23 | }); 24 | 25 | it('should accept function as assertion', function (done) { 26 | intoStream([1]) 27 | .pipe(assert.length(is.equal(1))) 28 | .pipe(assert.end(done)); 29 | }); 30 | }); 31 | -------------------------------------------------------------------------------- /test/stream.js: -------------------------------------------------------------------------------- 1 | /* global describe, it */ 2 | 3 | var assert = require('../assertStream'); 4 | var should = require('should'); 5 | 6 | describe('assert.stream', function () { 7 | it('should pass assertion event to next stream in pipeline', function (done) { 8 | var s1 = assert(); 9 | var s2 = assert(); 10 | 11 | s1.pipe(s2).on('assertion', function (error) { 12 | should.exist(error); 13 | done(); 14 | }); 15 | 16 | s1.emit('assertion', new Error('Bang!')); 17 | }); 18 | 19 | it('should emit end with error only once', function (done) { 20 | var s1 = assert(function (obj, enc, cb) { 21 | if (obj === 2) { 22 | this.assertion('Bang!'); 23 | } else { 24 | cb(null, obj); 25 | } 26 | }); 27 | var s2 = assert(); 28 | 29 | s1.pipe(s2).on('assertion', function (error) { 30 | should.exist(error); 31 | done(); 32 | }); 33 | 34 | s1.end(2); 35 | }); 36 | }); 37 | -------------------------------------------------------------------------------- /test/nth.js: -------------------------------------------------------------------------------- 1 | /* global describe, it */ 2 | 3 | var intoStream = require('into-stream').obj; 4 | var assert = require('../'); 5 | var should = require('should'); 6 | var is = require('funsert'); 7 | 8 | describe('assert.nth', function () { 9 | it('should check nth object', function (done) { 10 | intoStream([1]) 11 | .pipe(assert.nth(0, is.equal(1))) 12 | .pipe(assert.end(done)); 13 | }); 14 | 15 | it('should emit error on wrong nth object', function (done) { 16 | intoStream([1, 2]) 17 | .pipe(assert.nth(0, is.equal(2))) 18 | .pipe(assert.end(function (err) { 19 | should.exist(err); 20 | err.message.should.eql('0 position is not passing assertion: 1 is not equal 2'); 21 | done(); 22 | })); 23 | }); 24 | 25 | it('should have first shortcut', function (done) { 26 | intoStream([1, 2, 3]) 27 | .pipe(assert.first(is.equal(1))) 28 | .pipe(assert.end(done)); 29 | }); 30 | 31 | it('should have second shortcut', function (done) { 32 | intoStream([1, 2, 3]) 33 | .pipe(assert.second(is.equal(2))) 34 | .pipe(assert.end(done)); 35 | }); 36 | }); 37 | -------------------------------------------------------------------------------- /test/piping.js: -------------------------------------------------------------------------------- 1 | /* global describe, it */ 2 | 3 | var intoStream = require('into-stream').obj; 4 | var assert = require('../'); 5 | var should = require('should'); 6 | var is = require('funsert'); 7 | 8 | describe('assert piping', function () { 9 | it('should fail with first assertion', function (done) { 10 | intoStream([1, 2]) 11 | .pipe(assert.length(1)) 12 | .pipe(assert.length(2)) 13 | .pipe(assert.end(function (err) { 14 | should.exist(err); 15 | err.message.should.eql('Expected length 1 is not equal 2'); 16 | done(); 17 | })); 18 | }); 19 | 20 | it('should fail with second assertion', function (done) { 21 | intoStream([1, 2]) 22 | .pipe(assert.length(2)) 23 | .pipe(assert.length(1)) 24 | .pipe(assert.end(function (err) { 25 | should.exist(err); 26 | err.message.should.eql('Expected length 1 is not equal 2'); 27 | done(); 28 | })); 29 | }); 30 | 31 | it('should support piping after all', function (done) { 32 | intoStream([1, 1, 1]) 33 | .pipe(assert.all(is.equal(1))) 34 | .pipe(assert.length(3)) 35 | .pipe(assert.end(function (err) { 36 | should.not.exist(err); 37 | done(); 38 | })); 39 | }); 40 | }); 41 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # stream-assert 2 | [![Build Status][travis-image]][travis-url] 3 | 4 | Assert streams with ease. 5 | 6 | ## Usage 7 | 8 | ```js 9 | var intoStream = require('into-stream'); 10 | var assert = require('stream-assert'); 11 | var is = require('funsert'); 12 | 13 | intoStream([1, 2, 3]) 14 | .pipe(assert.first(is.equal(1))) 15 | .pipe(assert.second(is.equal(2))) 16 | .pipe(assert.nth(2, is.equal(3))) 17 | .pipe(assert.length(1)) 18 | .pipe(assert.end(console.log)); 19 | ``` 20 | 21 | ## Chaining 22 | 23 | Assertions are chained through passing `assertion` from pipe to pipe. If you want inject assertions in the middle of pipeline, you can attach `on('assertion')` handler to manualy catch assertions (instead of placing `assert.end`). 24 | 25 | 26 | ## API 27 | 28 | ### stream-assert 29 | 30 | Builder for asserting stream. 31 | 32 | #### nth(n, assertion) 33 | 34 | Calls `assertion` function on `nth` element in stream. 35 | 36 | #### first(assertion) 37 | > alias to nth(0, obj) 38 | 39 | #### second(assertion) 40 | > alias to nth(1, obj) 41 | 42 | #### last(assertion) 43 | 44 | Calls `assertion` function on the last element in stream. 45 | 46 | #### length(len) 47 | 48 | Asserting, that length of stream is equal `len` at the end of the stream. 49 | 50 | #### all(assertion) 51 | 52 | Checking that all elements in stream pass assertion function. 53 | 54 | #### any(assertion) 55 | 56 | Checking that at least one of elements in stream pass assertion function. 57 | 58 | #### end([cb]) 59 | 60 | Since streams has internal [buffer and highWatermark](http://nodejs.org/api/stream.html#stream_buffering), 61 | that stops data flow, when reached — test stream needs a dumping point, that will flush that buffer. 62 | 63 | `assert.end` will dump all data to `/dev/null` — so all pipes after this point will not get any data. 64 | 65 | ### assert.defaults 66 | Type: `Object` 67 | 68 | Contains defaults, that will be passed to `through` constructor. 69 | 70 | * `highWatermark` — by default, will be equal `16`. If you don't want to use `assert.end`, then you can increase it. 71 | 72 | ## License 73 | 74 | MIT (c) 2014 Vsevolod Strukchinsky 75 | 76 | [travis-url]: http://travis-ci.org/floatdrop/stream-assert 77 | [travis-image]: http://img.shields.io/travis/floatdrop/stream-assert.svg?branch=master&style=flat 78 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | var assertStream = require('./assertStream.js'); 2 | var assert = {}; 3 | 4 | function equal(expected) { 5 | if (typeof expected !== 'function') { 6 | return function (data) { 7 | if (data !== expected) { 8 | throw new Error(expected + ' is not equal ' + data); 9 | } 10 | }; 11 | } 12 | return expected; 13 | } 14 | 15 | var defaults = assert.defaults = { 16 | highWatermark: 16, 17 | objectMode: true 18 | }; 19 | 20 | assert.all = function (assertion) { 21 | var i = 0; 22 | return assertStream(defaults, function (obj, enc, cb) { 23 | try { 24 | assertion(obj); 25 | i++; 26 | cb(null, obj); 27 | } catch (err) { 28 | this.assertion('Element on ' + i + ' position is not passing assertion: ' + err.message); 29 | } 30 | }); 31 | }; 32 | 33 | assert.any = function (assertion) { 34 | var matched = false; 35 | return assertStream(defaults, function (obj, enc, cb) { 36 | try { 37 | assertion(obj); 38 | matched = true; 39 | } catch (err) { } 40 | cb(null, obj); 41 | }, function (cb) { 42 | if (!matched) { 43 | return this.assertion('Nothing passing assertion'); 44 | } 45 | cb(); 46 | }); 47 | }; 48 | 49 | assert.end = function (done) { 50 | var stream = assertStream(defaults, function (obj, enc, cb) { 51 | cb(); 52 | }); 53 | stream.on('finish', done || function () {}); 54 | stream.on('assertion', done || function () {}); 55 | return stream; 56 | }; 57 | 58 | assert.first = function (assertion) { 59 | return assert.nth(0, assertion); 60 | }; 61 | 62 | assert.last = function (assertion) { 63 | var lastItem; 64 | return assertStream(defaults, function (obj, enc, cb) { 65 | cb(null, lastItem = obj); 66 | }, function (cb) { 67 | try { 68 | assertion(lastItem); 69 | cb(); 70 | } catch (err) { 71 | this.assertion('Last element is not passing assertion: ' + err.message); 72 | } 73 | }); 74 | }; 75 | 76 | assert.length = function (expected) { 77 | var i = 0; 78 | return assertStream(defaults, function (obj, enc, cb) { 79 | i++; 80 | cb(null, obj); 81 | }, function (cb) { 82 | var assertion = equal(expected); 83 | 84 | try { 85 | assertion(i); 86 | cb(); 87 | } catch (err) { 88 | this.assertion('Expected length ' + err.message); 89 | } 90 | }); 91 | }; 92 | 93 | assert.nth = function (n, assertion) { 94 | var i = 0; 95 | return assertStream(defaults, function (obj, enc, cb) { 96 | if (i === n) { 97 | try { 98 | assertion(obj); 99 | i++; 100 | cb(null, obj); 101 | } catch (err) { 102 | this.assertion(n + ' position is not passing assertion: ' + err.message); 103 | } 104 | } else { 105 | i++; 106 | cb(null, obj); 107 | } 108 | }); 109 | }; 110 | 111 | assert.second = function (assertion) { 112 | return assert.nth(1, assertion); 113 | }; 114 | 115 | module.exports = assert; 116 | --------------------------------------------------------------------------------