vulcan 0.6.1 → 0.7.0
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/vulcan/cli.rb +20 -10
- data/lib/vulcan/version.rb +1 -1
- data/server/Procfile +1 -1
- data/server/bin/web +10 -0
- data/server/lib/cloudant.coffee +20 -0
- data/server/lib/logger.coffee +17 -0
- data/server/lib/on.coffee +10 -0
- data/server/lib/spawner.coffee +65 -0
- data/server/node_modules/coffee-script/CNAME +1 -0
- data/server/node_modules/coffee-script/LICENSE +22 -0
- data/server/node_modules/coffee-script/README +51 -0
- data/server/node_modules/coffee-script/Rakefile +78 -0
- data/server/node_modules/coffee-script/bin/cake +7 -0
- data/server/node_modules/coffee-script/bin/coffee +7 -0
- data/server/node_modules/coffee-script/extras/jsl.conf +44 -0
- data/server/node_modules/coffee-script/lib/coffee-script/browser.js +92 -0
- data/server/node_modules/coffee-script/lib/coffee-script/cake.js +111 -0
- data/server/node_modules/coffee-script/lib/coffee-script/coffee-script.js +167 -0
- data/server/node_modules/coffee-script/lib/coffee-script/command.js +500 -0
- data/server/node_modules/coffee-script/lib/coffee-script/grammar.js +606 -0
- data/server/node_modules/coffee-script/lib/coffee-script/helpers.js +77 -0
- data/server/node_modules/coffee-script/lib/coffee-script/index.js +11 -0
- data/server/node_modules/coffee-script/lib/coffee-script/lexer.js +788 -0
- data/server/node_modules/coffee-script/lib/coffee-script/nodes.js +2986 -0
- data/server/node_modules/coffee-script/lib/coffee-script/optparse.js +138 -0
- data/server/node_modules/coffee-script/lib/coffee-script/parser.js +683 -0
- data/server/node_modules/coffee-script/lib/coffee-script/repl.js +261 -0
- data/server/node_modules/coffee-script/lib/coffee-script/rewriter.js +349 -0
- data/server/node_modules/coffee-script/lib/coffee-script/scope.js +146 -0
- data/server/node_modules/coffee-script/package.json +55 -0
- data/server/node_modules/connect-form/History.md +0 -6
- data/server/node_modules/connect-form/lib/connect-form.js +2 -4
- data/server/node_modules/connect-form/node_modules/formidable/Readme.md +64 -36
- data/server/node_modules/connect-form/node_modules/formidable/lib/incoming_form.js +5 -1
- data/server/node_modules/connect-form/node_modules/formidable/package.json +20 -6
- data/server/node_modules/connect-form/node_modules/formidable/test/common.js +5 -6
- data/server/node_modules/connect-form/node_modules/formidable/test/fixture/file/funkyfilename.txt +1 -0
- data/server/node_modules/connect-form/node_modules/formidable/test/fixture/js/special-chars-in-filename.js +1 -1
- data/server/node_modules/connect-form/node_modules/formidable/test/{slow → integration}/test-fixtures.js +38 -33
- data/server/node_modules/connect-form/node_modules/formidable/test/legacy/simple/test-incoming-form.js +11 -0
- data/server/node_modules/connect-form/node_modules/formidable/test/run.js +1 -6
- data/server/node_modules/connect-form/node_modules/formidable/test/unit/test-incoming-form.js +63 -0
- data/server/node_modules/connect-form/package.json +27 -5
- data/server/node_modules/cradle/README.md +10 -10
- data/server/node_modules/cradle/lib/cradle.js +117 -523
- data/server/node_modules/cradle/lib/cradle/database/attachments.js +120 -0
- data/server/node_modules/cradle/lib/cradle/database/changes.js +56 -0
- data/server/node_modules/cradle/lib/cradle/database/documents.js +215 -0
- data/server/node_modules/cradle/lib/cradle/database/index.js +65 -0
- data/server/node_modules/cradle/lib/cradle/database/views.js +125 -0
- data/server/node_modules/cradle/node_modules/follow/LICENSE +202 -0
- data/server/node_modules/cradle/node_modules/follow/README.md +164 -0
- data/server/node_modules/cradle/node_modules/follow/Rakefile +54 -0
- data/server/node_modules/cradle/node_modules/follow/api.js +35 -0
- data/server/node_modules/cradle/node_modules/follow/browser/eventemitter2.js +453 -0
- data/server/node_modules/cradle/node_modules/follow/browser/export.js +78 -0
- data/server/node_modules/cradle/node_modules/follow/browser/index.html +14 -0
- data/server/node_modules/cradle/node_modules/follow/browser/jquery-1.6.1.min.js +18 -0
- data/server/node_modules/cradle/node_modules/follow/browser/log4js.js +46 -0
- data/server/node_modules/cradle/node_modules/follow/browser/main.js +92 -0
- data/server/node_modules/cradle/node_modules/follow/browser/querystring.js +28 -0
- data/server/node_modules/cradle/node_modules/follow/browser/request.jquery.js +237 -0
- data/server/node_modules/cradle/node_modules/follow/browser/require.js +33 -0
- data/server/node_modules/cradle/node_modules/follow/browser/util.js +28 -0
- data/server/node_modules/cradle/node_modules/follow/cli.js +101 -0
- data/server/node_modules/cradle/node_modules/follow/lib/feed.js +556 -0
- data/server/node_modules/cradle/node_modules/follow/lib/index.js +66 -0
- data/server/node_modules/cradle/node_modules/follow/lib/stream.js +305 -0
- data/server/node_modules/cradle/node_modules/follow/node_modules/request/LICENSE +55 -0
- data/server/node_modules/cradle/node_modules/follow/node_modules/request/README.md +285 -0
- data/server/node_modules/cradle/node_modules/follow/node_modules/request/main.js +618 -0
- data/server/node_modules/cradle/node_modules/follow/node_modules/request/mimetypes.js +146 -0
- data/server/node_modules/cradle/node_modules/follow/node_modules/request/oauth.js +34 -0
- data/server/node_modules/cradle/node_modules/follow/node_modules/request/package.json +42 -0
- data/server/node_modules/cradle/node_modules/follow/node_modules/request/tests/googledoodle.png +0 -0
- data/server/node_modules/cradle/node_modules/follow/node_modules/request/tests/run.sh +6 -0
- data/server/node_modules/cradle/node_modules/follow/node_modules/request/tests/server.js +57 -0
- data/server/node_modules/cradle/node_modules/follow/node_modules/request/tests/test-body.js +90 -0
- data/server/node_modules/cradle/node_modules/follow/node_modules/request/tests/test-cookie.js +29 -0
- data/server/node_modules/cradle/node_modules/follow/node_modules/request/tests/test-cookiejar.js +90 -0
- data/server/node_modules/cradle/node_modules/follow/node_modules/request/tests/test-errors.js +30 -0
- data/server/node_modules/cradle/node_modules/follow/node_modules/request/tests/test-oauth.js +109 -0
- data/server/node_modules/cradle/node_modules/follow/node_modules/request/tests/test-pipes.js +167 -0
- data/server/node_modules/cradle/node_modules/follow/node_modules/request/tests/test-proxy.js +39 -0
- data/server/node_modules/cradle/node_modules/follow/node_modules/request/tests/test-timeout.js +87 -0
- data/server/node_modules/cradle/node_modules/follow/node_modules/request/uuid.js +19 -0
- data/server/node_modules/cradle/node_modules/follow/node_modules/request/vendor/cookie/index.js +55 -0
- data/server/node_modules/cradle/node_modules/follow/node_modules/request/vendor/cookie/jar.js +72 -0
- data/server/node_modules/cradle/node_modules/follow/package.json +45 -0
- data/server/node_modules/cradle/node_modules/follow/test/couch.js +153 -0
- data/server/node_modules/cradle/node_modules/follow/test/follow.js +136 -0
- data/server/node_modules/cradle/node_modules/follow/test/issues.js +178 -0
- data/server/node_modules/cradle/node_modules/follow/test/issues/10.js +24 -0
- data/server/node_modules/cradle/node_modules/follow/test/stream.js +493 -0
- data/server/node_modules/cradle/node_modules/request/LICENSE +55 -0
- data/server/node_modules/cradle/node_modules/request/README.md +287 -0
- data/server/node_modules/cradle/node_modules/request/forever.js +103 -0
- data/server/node_modules/cradle/node_modules/request/main.js +913 -0
- data/server/node_modules/cradle/node_modules/request/mimetypes.js +152 -0
- data/server/node_modules/cradle/node_modules/request/oauth.js +34 -0
- data/server/node_modules/cradle/node_modules/request/package.json +42 -0
- data/server/node_modules/cradle/node_modules/request/tests/googledoodle.png +0 -0
- data/server/node_modules/cradle/node_modules/request/tests/run.js +38 -0
- data/server/node_modules/cradle/node_modules/request/tests/server.js +82 -0
- data/server/node_modules/cradle/node_modules/request/tests/squid.conf +77 -0
- data/server/node_modules/cradle/node_modules/request/tests/ssl/ca/ca.cnf +20 -0
- data/server/node_modules/cradle/node_modules/request/tests/ssl/ca/ca.crl +0 -0
- data/server/node_modules/cradle/node_modules/request/tests/ssl/ca/ca.crt +17 -0
- data/server/node_modules/cradle/node_modules/request/tests/ssl/ca/ca.csr +13 -0
- data/server/node_modules/cradle/node_modules/request/tests/ssl/ca/ca.key +18 -0
- data/server/node_modules/cradle/node_modules/request/tests/ssl/ca/ca.srl +1 -0
- data/server/node_modules/cradle/node_modules/request/tests/ssl/ca/server.cnf +19 -0
- data/server/node_modules/cradle/node_modules/request/tests/ssl/ca/server.crt +16 -0
- data/server/node_modules/cradle/node_modules/request/tests/ssl/ca/server.csr +11 -0
- data/server/node_modules/cradle/node_modules/request/tests/ssl/ca/server.js +28 -0
- data/server/node_modules/cradle/node_modules/request/tests/ssl/ca/server.key +9 -0
- data/server/node_modules/cradle/node_modules/request/tests/ssl/npm-ca.crt +16 -0
- data/server/node_modules/cradle/node_modules/request/tests/ssl/test.crt +15 -0
- data/server/node_modules/cradle/node_modules/request/tests/ssl/test.key +15 -0
- data/server/node_modules/cradle/node_modules/request/tests/test-body.js +95 -0
- data/server/node_modules/cradle/node_modules/request/tests/test-cookie.js +29 -0
- data/server/node_modules/cradle/node_modules/request/tests/test-cookiejar.js +90 -0
- data/server/node_modules/cradle/node_modules/request/tests/test-defaults.js +68 -0
- data/server/node_modules/cradle/node_modules/request/tests/test-errors.js +37 -0
- data/server/node_modules/cradle/node_modules/request/tests/test-headers.js +52 -0
- data/server/node_modules/cradle/node_modules/request/tests/test-httpModule.js +94 -0
- data/server/node_modules/cradle/node_modules/request/tests/test-https-strict.js +97 -0
- data/server/node_modules/cradle/node_modules/request/tests/test-https.js +86 -0
- data/server/node_modules/cradle/node_modules/request/tests/test-oauth.js +117 -0
- data/server/node_modules/cradle/node_modules/request/tests/test-params.js +92 -0
- data/server/node_modules/cradle/node_modules/request/tests/test-pipes.js +202 -0
- data/server/node_modules/cradle/node_modules/request/tests/test-proxy.js +39 -0
- data/server/node_modules/cradle/node_modules/request/tests/test-qs.js +28 -0
- data/server/node_modules/cradle/node_modules/request/tests/test-redirect.js +154 -0
- data/server/node_modules/cradle/node_modules/request/tests/test-timeout.js +87 -0
- data/server/node_modules/cradle/node_modules/request/tests/test-toJSON.js +14 -0
- data/server/node_modules/cradle/node_modules/request/tests/test-tunnel.js +61 -0
- data/server/node_modules/cradle/node_modules/request/tunnel.js +229 -0
- data/server/node_modules/cradle/node_modules/request/uuid.js +19 -0
- data/server/node_modules/cradle/node_modules/request/vendor/cookie/index.js +65 -0
- data/server/node_modules/cradle/node_modules/request/vendor/cookie/jar.js +72 -0
- data/server/node_modules/cradle/node_modules/vargs/package.json +33 -10
- data/server/node_modules/cradle/package.json +50 -12
- data/server/node_modules/cradle/test/cache-test.js +1 -4
- data/server/node_modules/cradle/test/connection-test.js +179 -0
- data/server/node_modules/cradle/test/database-attachment-test.js +344 -0
- data/server/node_modules/cradle/test/database-cache-test.js +132 -0
- data/server/node_modules/cradle/test/database-test.js +219 -0
- data/server/node_modules/cradle/test/database-view-test.js +141 -0
- data/server/node_modules/cradle/test/fixtures/databases.json +28 -1
- data/server/node_modules/cradle/test/helpers/seed.js +14 -5
- data/server/node_modules/cradle/test/response-test.js +1 -1
- data/server/node_modules/express/History.md +16 -0
- data/server/node_modules/express/bin/express +7 -6
- data/server/node_modules/express/lib-cov/application.js +510 -0
- data/server/node_modules/express/lib-cov/express.js +65 -0
- data/server/node_modules/express/lib-cov/middleware.js +54 -0
- data/server/node_modules/express/lib-cov/request.js +225 -0
- data/server/node_modules/express/lib-cov/response.js +611 -0
- data/server/node_modules/express/lib-cov/router/collection.js +40 -0
- data/server/node_modules/express/lib-cov/router/index.js +515 -0
- data/server/node_modules/express/lib-cov/router/methods.js +9 -0
- data/server/node_modules/express/lib-cov/router/route.js +68 -0
- data/server/node_modules/express/lib-cov/utils.js +151 -0
- data/server/node_modules/express/lib-cov/view.js +81 -0
- data/server/node_modules/express/lib/express.js +1 -1
- data/server/node_modules/express/lib/http.js +1 -2
- data/server/node_modules/express/lib/request.js +2 -2
- data/server/node_modules/express/lib/router/methods.js +10 -1
- data/server/node_modules/express/node_modules/connect/lib/connect.js +1 -1
- data/server/node_modules/express/node_modules/connect/lib/http.js +3 -2
- data/server/node_modules/express/node_modules/connect/lib/middleware/limit.js +0 -2
- data/server/node_modules/express/node_modules/connect/lib/middleware/session.js +1 -2
- data/server/node_modules/express/node_modules/connect/node_modules/formidable/Readme.md +42 -25
- data/server/node_modules/express/node_modules/connect/node_modules/formidable/lib/incoming_form.js +1 -0
- data/server/node_modules/express/node_modules/connect/node_modules/formidable/package.json +14 -3
- data/server/node_modules/express/node_modules/connect/node_modules/formidable/test/legacy/simple/test-incoming-form.js +11 -0
- data/server/node_modules/express/node_modules/connect/package.json +35 -7
- data/server/node_modules/express/node_modules/mime/package.json +30 -8
- data/server/node_modules/express/node_modules/mkdirp/README.markdown +35 -2
- data/server/node_modules/express/node_modules/mkdirp/examples/pow.js +1 -1
- data/server/node_modules/express/node_modules/mkdirp/index.js +72 -13
- data/server/node_modules/express/node_modules/mkdirp/package.json +39 -21
- data/server/node_modules/express/node_modules/mkdirp/test/chmod.js +38 -0
- data/server/node_modules/express/node_modules/mkdirp/test/clobber.js +37 -0
- data/server/node_modules/express/node_modules/mkdirp/test/perm.js +32 -0
- data/server/node_modules/express/node_modules/mkdirp/test/perm_sync.js +39 -0
- data/server/node_modules/express/node_modules/mkdirp/test/sync.js +27 -0
- data/server/node_modules/express/node_modules/mkdirp/test/umask.js +28 -0
- data/server/node_modules/express/node_modules/mkdirp/test/umask_sync.js +27 -0
- data/server/node_modules/express/node_modules/qs/History.md +10 -0
- data/server/node_modules/express/node_modules/qs/Readme.md +9 -2
- data/server/node_modules/express/node_modules/qs/examples.js +3 -0
- data/server/node_modules/express/node_modules/qs/lib/querystring.js +8 -6
- data/server/node_modules/express/node_modules/qs/package.json +26 -8
- data/server/node_modules/express/node_modules/qs/test/parse.js +13 -1
- data/server/node_modules/express/node_modules/qs/test/stringify.js +45 -37
- data/server/node_modules/express/package.json +55 -16
- data/server/node_modules/express/test.js +41 -0
- data/server/node_modules/knox/package.json +26 -4
- data/server/node_modules/node-uuid/package.json +40 -11
- data/server/node_modules/node-uuid/test/test.js +1 -1
- data/server/node_modules/nodemon/README.md +120 -0
- data/server/node_modules/nodemon/nodemon.js +518 -0
- data/server/node_modules/nodemon/nodemonignore.example +11 -0
- data/server/node_modules/nodemon/package.json +49 -0
- data/server/node_modules/restler/README.md +144 -94
- data/server/node_modules/restler/lib/multipartform.js +2 -0
- data/server/node_modules/restler/lib/restler.js +218 -61
- data/server/node_modules/restler/package.json +35 -8
- data/server/node_modules/restler/test/all.js +6 -1
- data/server/node_modules/restler/test/restler.js +624 -118
- data/server/package.json +14 -10
- data/server/web.coffee +64 -0
- data/server/web.js +15 -3
- metadata +170 -57
- data/server/index.js +0 -14
- data/server/node_modules/connect-form/LICENSE +0 -22
- data/server/node_modules/connect-form/index.js +0 -100
- data/server/node_modules/connect-form/node_modules/formidable/test/fast/test-incoming-form.js +0 -45
- data/server/node_modules/connect-form/node_modules/formidable/test/fixture/http/no-filename/generic.http +0 -13
- data/server/node_modules/connect-form/node_modules/formidable/test/fixture/http/special-chars-in-filename/osx-chrome-13.http +0 -26
- data/server/node_modules/connect-form/node_modules/formidable/test/fixture/http/special-chars-in-filename/osx-firefox-3.6.http +0 -24
- data/server/node_modules/connect-form/node_modules/formidable/test/fixture/http/special-chars-in-filename/osx-safari-5.http +0 -23
- data/server/node_modules/connect-form/node_modules/formidable/test/fixture/http/special-chars-in-filename/xp-chrome-12.http +0 -24
- data/server/node_modules/connect-form/node_modules/formidable/test/fixture/http/special-chars-in-filename/xp-ie-7.http +0 -22
- data/server/node_modules/connect-form/node_modules/formidable/test/fixture/http/special-chars-in-filename/xp-ie-8.http +0 -22
- data/server/node_modules/connect-form/node_modules/formidable/test/fixture/http/special-chars-in-filename/xp-safari-5.http +0 -22
- data/server/node_modules/connect-form/node_modules/formidable/test/fixture/multi_video.upload +0 -0
- data/server/node_modules/cradle/Makefile +0 -10
- data/server/node_modules/cradle/test/cradle-test.js +0 -650
- data/server/node_modules/express/node_modules/connect/node_modules/formidable/test/fixture/http/no-filename/generic.http +0 -13
- data/server/node_modules/express/node_modules/connect/node_modules/formidable/test/fixture/http/special-chars-in-filename/osx-chrome-13.http +0 -26
- data/server/node_modules/express/node_modules/connect/node_modules/formidable/test/fixture/http/special-chars-in-filename/osx-firefox-3.6.http +0 -24
- data/server/node_modules/express/node_modules/connect/node_modules/formidable/test/fixture/http/special-chars-in-filename/osx-safari-5.http +0 -23
- data/server/node_modules/express/node_modules/connect/node_modules/formidable/test/fixture/http/special-chars-in-filename/xp-chrome-12.http +0 -24
- data/server/node_modules/express/node_modules/connect/node_modules/formidable/test/fixture/http/special-chars-in-filename/xp-ie-7.http +0 -22
- data/server/node_modules/express/node_modules/connect/node_modules/formidable/test/fixture/http/special-chars-in-filename/xp-ie-8.http +0 -22
- data/server/node_modules/express/node_modules/connect/node_modules/formidable/test/fixture/http/special-chars-in-filename/xp-safari-5.http +0 -22
- data/server/node_modules/express/node_modules/connect/node_modules/formidable/test/fixture/multi_video.upload +0 -0
- data/server/node_modules/express/node_modules/connect/test.js +0 -52
- data/server/node_modules/express/testing/foo/app.js +0 -35
- data/server/node_modules/express/testing/foo/package.json +0 -9
- data/server/node_modules/express/testing/foo/public/stylesheets/style.css +0 -8
- data/server/node_modules/express/testing/foo/routes/index.js +0 -10
- data/server/node_modules/express/testing/foo/views/index.jade +0 -2
- data/server/node_modules/express/testing/foo/views/layout.jade +0 -6
- data/server/node_modules/express/testing/index.js +0 -43
- data/server/node_modules/express/testing/public/test.txt +0 -2971
- data/server/node_modules/express/testing/views/page.html +0 -1
- data/server/node_modules/express/testing/views/page.jade +0 -3
- data/server/node_modules/express/testing/views/test.md +0 -1
- data/server/node_modules/express/testing/views/user/index.jade +0 -1
- data/server/node_modules/express/testing/views/user/list.jade +0 -1
- data/server/node_modules/knox/lib/knox/mime/index.js +0 -308
- data/server/node_modules/knox/lib/knox/mime/test.js +0 -59
- data/server/node_modules/node-uuid/test/benchmark-native +0 -0
- data/server/node_modules/on/index.js +0 -13
- data/server/node_modules/restler/test/test_helper.js +0 -163
- data/server/node_modules/spawner/index.js +0 -106
@@ -0,0 +1,178 @@
|
|
1
|
+
var tap = require('tap')
|
2
|
+
, test = tap.test
|
3
|
+
, util = require('util')
|
4
|
+
, request = require('request')
|
5
|
+
, traceback = require('traceback')
|
6
|
+
|
7
|
+
var lib = require('../lib')
|
8
|
+
, couch = require('./couch')
|
9
|
+
, follow = require('../api')
|
10
|
+
|
11
|
+
couch.setup(test)
|
12
|
+
|
13
|
+
test('Issue #5', function(t) {
|
14
|
+
var saw = { loops:0, seqs:{} }
|
15
|
+
|
16
|
+
var saw_change = false
|
17
|
+
// -2 means I want to see the last change.
|
18
|
+
var feed = follow({'db':couch.DB, since:-2}, function(er, change) {
|
19
|
+
t.equal(change.seq, 3, 'Got the latest change, 3')
|
20
|
+
t.false(saw_change, 'Only one callback run for since=-2 (assuming no subsequent change')
|
21
|
+
saw_change = true
|
22
|
+
|
23
|
+
process.nextTick(function() { feed.stop() })
|
24
|
+
feed.on('stop', function() {
|
25
|
+
// Test using since=-1 (AKA since="now").
|
26
|
+
follow({'db':couch.DB, since:'now'}, function(er, change) {
|
27
|
+
t.equal(change.seq, 4, 'Only get changes made after starting the feed')
|
28
|
+
t.equal(change.id, "You're in now, now", 'Got the subsequent change')
|
29
|
+
|
30
|
+
this.stop()
|
31
|
+
t.end()
|
32
|
+
})
|
33
|
+
|
34
|
+
// Let that follower settle in, then send it something
|
35
|
+
setTimeout(function() {
|
36
|
+
var doc = { _id:"You're in now, now", movie:"Spaceballs" }
|
37
|
+
request.post({uri:couch.DB, json:doc}, function(er) {
|
38
|
+
if(er) throw er
|
39
|
+
})
|
40
|
+
}, couch.rtt())
|
41
|
+
})
|
42
|
+
})
|
43
|
+
})
|
44
|
+
|
45
|
+
couch.setup(test) // Back to the expected documents
|
46
|
+
|
47
|
+
test('Issue #6', function(t) {
|
48
|
+
// When we see change 1, delete the database. The rest should still come in, then the error indicating deletion.
|
49
|
+
var saw = { seqs:{}, redid:false, redo_err:null }
|
50
|
+
|
51
|
+
follow(couch.DB, function(er, change) {
|
52
|
+
if(!er) {
|
53
|
+
saw.seqs[change.seq] = true
|
54
|
+
t.notOk(change.last_seq, 'Change '+change.seq+' ha no .last_seq')
|
55
|
+
if(change.seq == 1) {
|
56
|
+
couch.redo(function(er) {
|
57
|
+
saw.redid = true
|
58
|
+
saw.redo_err = er
|
59
|
+
})
|
60
|
+
}
|
61
|
+
}
|
62
|
+
|
63
|
+
else setTimeout(function() {
|
64
|
+
// Give the redo time to finish, then confirm that everything happened as expected.
|
65
|
+
// Hopefully this error indicates the database was deleted.
|
66
|
+
t.ok(er.message.match(/deleted .* 3$/), 'Got delete error after change 3')
|
67
|
+
t.ok(er.deleted, 'Error object indicates database deletion')
|
68
|
+
t.equal(er.last_seq, 3, 'Error object indicates the last change number')
|
69
|
+
|
70
|
+
t.ok(saw.seqs[1], 'Change 1 was processed')
|
71
|
+
t.ok(saw.seqs[2], 'Change 2 was processed')
|
72
|
+
t.ok(saw.seqs[3], 'Change 3 was processed')
|
73
|
+
t.ok(saw.redid, 'The redo function ran')
|
74
|
+
t.false(saw.redo_err, 'No problem redoing the database')
|
75
|
+
|
76
|
+
return t.end()
|
77
|
+
}, couch.rtt() * 2)
|
78
|
+
})
|
79
|
+
})
|
80
|
+
|
81
|
+
test('Issue #8', function(t) {
|
82
|
+
var timeouts = timeout_tracker()
|
83
|
+
|
84
|
+
// Detect inappropriate timeouts after the run.
|
85
|
+
var runs = {'set':false, 'clear':false}
|
86
|
+
function badSetT() {
|
87
|
+
runs.set = true
|
88
|
+
return setTimeout.apply(this, arguments)
|
89
|
+
}
|
90
|
+
|
91
|
+
function badClearT() {
|
92
|
+
runs.clear = true
|
93
|
+
return clearTimeout.apply(this, arguments)
|
94
|
+
}
|
95
|
+
|
96
|
+
follow(couch.DB, function(er, change) {
|
97
|
+
t.false(er, 'Got a feed')
|
98
|
+
t.equal(change.seq, 1, 'Handler only runs for one change')
|
99
|
+
|
100
|
+
this.on('stop', check_timeouts)
|
101
|
+
this.stop()
|
102
|
+
|
103
|
+
function check_timeouts() {
|
104
|
+
t.equal(timeouts().length, 0, 'No timeouts by the time stop fires')
|
105
|
+
|
106
|
+
lib.timeouts(badSetT, badClearT)
|
107
|
+
|
108
|
+
// And give it a moment to try something bad.
|
109
|
+
setTimeout(final_timeout_check, 250)
|
110
|
+
function final_timeout_check() {
|
111
|
+
t.equal(timeouts().length, 0, 'No lingering timeouts after teardown: ' + tims(timeouts()))
|
112
|
+
t.false(runs.set, 'No more setTimeouts ran')
|
113
|
+
t.false(runs.clear, 'No more clearTimeouts ran')
|
114
|
+
|
115
|
+
t.end()
|
116
|
+
}
|
117
|
+
}
|
118
|
+
})
|
119
|
+
})
|
120
|
+
|
121
|
+
test('Issue #9', function(t) {
|
122
|
+
var timeouts = timeout_tracker()
|
123
|
+
|
124
|
+
follow({db:couch.DB, inactivity_ms:30000}, function(er, change) {
|
125
|
+
if(change.seq == 1)
|
126
|
+
return // Let it run through once, just for fun.
|
127
|
+
|
128
|
+
t.equal(change.seq, 2, 'The second change will be the last')
|
129
|
+
this.stop()
|
130
|
+
|
131
|
+
setTimeout(check_inactivity_timer, 250)
|
132
|
+
function check_inactivity_timer() {
|
133
|
+
t.equal(timeouts().length, 0, 'No lingering timeouts after teardown: ' + tims(timeouts()))
|
134
|
+
timeouts().forEach(function(id) { clearTimeout(id) })
|
135
|
+
t.end()
|
136
|
+
}
|
137
|
+
})
|
138
|
+
})
|
139
|
+
|
140
|
+
//
|
141
|
+
// Utilities
|
142
|
+
//
|
143
|
+
|
144
|
+
function timeout_tracker() {
|
145
|
+
// Return an array tracking in-flight timeouts.
|
146
|
+
var timeouts = []
|
147
|
+
var set_num = 0
|
148
|
+
|
149
|
+
lib.timeouts(set, clear)
|
150
|
+
return function() { return timeouts }
|
151
|
+
|
152
|
+
function set() {
|
153
|
+
var result = setTimeout.apply(this, arguments)
|
154
|
+
|
155
|
+
var caller = traceback()[2]
|
156
|
+
set_num += 1
|
157
|
+
result.caller = '('+set_num+') ' + (caller.method || caller.name || '<a>') + ' in ' + caller.file + ':' + caller.line
|
158
|
+
//console.error('setTimeout: ' + result.caller)
|
159
|
+
|
160
|
+
timeouts.push(result)
|
161
|
+
//console.error('inflight ('+timeouts.length+'): ' + tims(timeouts))
|
162
|
+
return result
|
163
|
+
}
|
164
|
+
|
165
|
+
function clear(id) {
|
166
|
+
//var caller = traceback()[2]
|
167
|
+
//caller = (caller.method || caller.name || '<a>') + ' in ' + caller.file + ':' + caller.line
|
168
|
+
//console.error('clearTimeout: ' + (id && id.caller) + ' <- ' + caller)
|
169
|
+
|
170
|
+
timeouts = timeouts.filter(function(element) { return element !== id })
|
171
|
+
//console.error('inflight ('+timeouts.length+'): ' + tims(timeouts))
|
172
|
+
return clearTimeout.apply(this, arguments)
|
173
|
+
}
|
174
|
+
}
|
175
|
+
|
176
|
+
function tims(arr) {
|
177
|
+
return JSON.stringify(arr.map(function(timer) { return timer.caller }))
|
178
|
+
}
|
@@ -0,0 +1,24 @@
|
|
1
|
+
var tap = require('tap')
|
2
|
+
, test = tap.test
|
3
|
+
, util = require('util')
|
4
|
+
|
5
|
+
// Issue #10 is about missing log4js. This file sets the environment variable to disable it.
|
6
|
+
process.env.log_plain = true
|
7
|
+
|
8
|
+
var lib = require('../../lib')
|
9
|
+
, couch = require('../couch')
|
10
|
+
, follow = require('../../api')
|
11
|
+
|
12
|
+
couch.setup(test)
|
13
|
+
|
14
|
+
test('Issue #10', function(t) {
|
15
|
+
follow({db:couch.DB, inactivity_ms:30000}, function(er, change) {
|
16
|
+
console.error('Change: ' + JSON.stringify(change))
|
17
|
+
if(change.seq == 2)
|
18
|
+
this.stop()
|
19
|
+
|
20
|
+
this.on('stop', function() {
|
21
|
+
t.end()
|
22
|
+
})
|
23
|
+
})
|
24
|
+
})
|
@@ -0,0 +1,493 @@
|
|
1
|
+
var tap = require('tap')
|
2
|
+
, test = tap.test
|
3
|
+
, util = require('util')
|
4
|
+
, request = require('request')
|
5
|
+
|
6
|
+
var couch = require('./couch')
|
7
|
+
, follow = require('../api')
|
8
|
+
|
9
|
+
|
10
|
+
couch.setup(test)
|
11
|
+
|
12
|
+
test('The Changes stream API', function(t) {
|
13
|
+
var feed = new follow.Changes
|
14
|
+
|
15
|
+
t.type(feed.statusCode, 'null', 'Changes has a .statusCode (initially null)')
|
16
|
+
t.type(feed.setHeader, 'function', 'Changes has a .setHeader() method')
|
17
|
+
t.type(feed.headers, 'object', 'Changes has a .headers object')
|
18
|
+
t.same(feed.headers, {}, 'Changes headers are initially empty')
|
19
|
+
|
20
|
+
t.end()
|
21
|
+
})
|
22
|
+
|
23
|
+
test('Readable Stream API', function(t) {
|
24
|
+
var feed = new follow.Changes
|
25
|
+
|
26
|
+
t.is(feed.readable, true, 'Changes is a readable stream')
|
27
|
+
|
28
|
+
t.type(feed.setEncoding, 'function', 'Changes has .setEncoding() method')
|
29
|
+
t.type(feed.pause, 'function', 'Changes has .pause() method')
|
30
|
+
t.type(feed.resume, 'function', 'Changes has .resume() method')
|
31
|
+
t.type(feed.destroy, 'function', 'Changes has .destroy() method')
|
32
|
+
t.type(feed.destroySoon, 'function', 'Changes has .destroySoon() method')
|
33
|
+
t.type(feed.pipe, 'function', 'Changes has .pipe() method')
|
34
|
+
|
35
|
+
t.end()
|
36
|
+
})
|
37
|
+
|
38
|
+
test('Writatable Stream API', function(t) {
|
39
|
+
var feed = new follow.Changes
|
40
|
+
|
41
|
+
t.is(feed.writable, true, 'Changes is a writable stream')
|
42
|
+
|
43
|
+
t.type(feed.write, 'function', 'Changes has .write() method')
|
44
|
+
t.type(feed.end, 'function', 'Changes has .end() method')
|
45
|
+
t.type(feed.destroy, 'function', 'Changes has .destroy() method')
|
46
|
+
t.type(feed.destroySoon, 'function', 'Changes has .destroySoon() method')
|
47
|
+
|
48
|
+
t.end()
|
49
|
+
})
|
50
|
+
|
51
|
+
test('Error conditions', function(t) {
|
52
|
+
var feed = new follow.Changes
|
53
|
+
t.throws(write, 'Throw if the feed type is not defined')
|
54
|
+
|
55
|
+
feed = new follow.Changes
|
56
|
+
feed.feed = 'neither longpoll nor continuous'
|
57
|
+
t.throws(write, 'Throw if the feed type is not longpoll nor continuous')
|
58
|
+
|
59
|
+
feed = new follow.Changes({'feed':'longpoll'})
|
60
|
+
t.throws(write('stuff'), 'Throw if the "results" line is not sent first')
|
61
|
+
|
62
|
+
feed = new follow.Changes({'feed':'longpoll'})
|
63
|
+
t.doesNotThrow(write('') , 'Empty string is fine waiting for "results"')
|
64
|
+
t.doesNotThrow(write('{') , 'This could be the "results" line')
|
65
|
+
t.doesNotThrow(write('"resu', 'Another part of the "results" line'))
|
66
|
+
t.doesNotThrow(write('') , 'Another empty string is still fine')
|
67
|
+
t.doesNotThrow(write('lts":', 'Final part of "results" line still good'))
|
68
|
+
t.throws(write(']'), 'First line was not {"results":[')
|
69
|
+
|
70
|
+
feed = new follow.Changes
|
71
|
+
feed.feed = 'continuous'
|
72
|
+
t.doesNotThrow(write(''), 'Empty string is fine for a continuous feed')
|
73
|
+
t.throws(end('{"results":[\n'), 'Continuous stream does not want a header')
|
74
|
+
|
75
|
+
feed = new follow.Changes({'feed':'continuous'})
|
76
|
+
t.throws(write('hi\n'), 'Continuous stream wants objects')
|
77
|
+
|
78
|
+
feed = new follow.Changes({'feed':'continuous'})
|
79
|
+
t.throws(end('[]\n'), 'Continuous stream wants "real" objects, not Array')
|
80
|
+
|
81
|
+
feed = new follow.Changes({'feed':'continuous'})
|
82
|
+
t.throws(write('{"seq":1,"id":"hi","changes":[{"rev":"1-869df2efe56ff5228e613ceb4d561b35"}]},\n'),
|
83
|
+
'Continuous stream does not want a comma')
|
84
|
+
|
85
|
+
var types = ['longpoll', 'continuous']
|
86
|
+
types.forEach(function(type) {
|
87
|
+
var bad_writes = [ {}, null, ['a string (array)'], {'an':'object'}]
|
88
|
+
bad_writes.forEach(function(obj) {
|
89
|
+
feed = new follow.Changes
|
90
|
+
feed.feed = type
|
91
|
+
|
92
|
+
t.throws(write(obj), 'Throw for bad write to '+type+': ' + util.inspect(obj))
|
93
|
+
})
|
94
|
+
|
95
|
+
feed = new follow.Changes
|
96
|
+
feed.feed = type
|
97
|
+
|
98
|
+
var valid = (type == 'longpoll')
|
99
|
+
? '{"results":[\n{}\n],\n"last_seq":1}'
|
100
|
+
: '{"seq":1,"id":"doc"}'
|
101
|
+
|
102
|
+
t.throws(buf(valid, 'but_invalid_encoding'), 'Throw for buffer with bad encoding')
|
103
|
+
})
|
104
|
+
|
105
|
+
t.end()
|
106
|
+
|
107
|
+
function buf(data, encoding) {
|
108
|
+
return write(new Buffer(data), encoding)
|
109
|
+
}
|
110
|
+
|
111
|
+
function write(data, encoding) {
|
112
|
+
if(data === undefined)
|
113
|
+
return feed.write('blah')
|
114
|
+
return function() { feed.write(data, encoding) }
|
115
|
+
}
|
116
|
+
|
117
|
+
function end(data, encoding) {
|
118
|
+
return function() { feed.end(data, encoding) }
|
119
|
+
}
|
120
|
+
})
|
121
|
+
|
122
|
+
test('Longpoll feed', function(t) {
|
123
|
+
for(var i = 0; i < 2; i++) {
|
124
|
+
var feed = new follow.Changes({'feed':'longpoll'})
|
125
|
+
|
126
|
+
var data = []
|
127
|
+
feed.on('data', function(d) { data.push(d) })
|
128
|
+
|
129
|
+
function encode(data) { return (i == 0) ? data : new Buffer(data) }
|
130
|
+
function write(data) { return function() { feed.write(encode(data)) } }
|
131
|
+
function end(data) { return function() { feed.end(encode(data)) } }
|
132
|
+
|
133
|
+
t.doesNotThrow(write('{"results":[') , 'Longpoll header')
|
134
|
+
t.doesNotThrow(write('{}') , 'Empty object')
|
135
|
+
t.doesNotThrow(write(',{"foo":"bar"},') , 'Comma prefix and suffix')
|
136
|
+
t.doesNotThrow(write('{"two":"bar"},') , 'Comma suffix')
|
137
|
+
t.doesNotThrow(write('{"three":3},{"four":4}'), 'Two objects on one line')
|
138
|
+
t.doesNotThrow(end('],\n"last_seq":3}\n') , 'Longpoll footer')
|
139
|
+
|
140
|
+
t.equal(data.length, 5, 'Five data events fired')
|
141
|
+
t.equal(data[0], '{}', 'First object emitted')
|
142
|
+
t.equal(data[1], '{"foo":"bar"}', 'Second object emitted')
|
143
|
+
t.equal(data[2], '{"two":"bar"}', 'Third object emitted')
|
144
|
+
t.equal(data[3], '{"three":3}', 'Fourth object emitted')
|
145
|
+
t.equal(data[4], '{"four":4}', 'Fifth object emitted')
|
146
|
+
}
|
147
|
+
|
148
|
+
t.end()
|
149
|
+
})
|
150
|
+
|
151
|
+
test('Longpoll pause', function(t) {
|
152
|
+
var feed = new follow.Changes({'feed':'longpoll'})
|
153
|
+
, all = {'results':[{'change':1}, {'second':'change'},{'change':'#3'}], 'last_seq':3}
|
154
|
+
, start = new Date
|
155
|
+
|
156
|
+
var events = []
|
157
|
+
|
158
|
+
feed.on('data', function(change) {
|
159
|
+
change = JSON.parse(change)
|
160
|
+
change.elapsed = new Date - start
|
161
|
+
events.push(change)
|
162
|
+
})
|
163
|
+
|
164
|
+
feed.once('data', function(data) {
|
165
|
+
t.equal(data, '{"change":1}', 'First data event was the first change')
|
166
|
+
feed.pause()
|
167
|
+
setTimeout(function() { feed.resume() }, 100)
|
168
|
+
})
|
169
|
+
|
170
|
+
feed.on('end', function() {
|
171
|
+
t.equal(feed.readable, false, 'Feed is no longer readable')
|
172
|
+
events.push('END')
|
173
|
+
})
|
174
|
+
|
175
|
+
setTimeout(check_events, 150)
|
176
|
+
feed.end(JSON.stringify(all))
|
177
|
+
|
178
|
+
function check_events() {
|
179
|
+
t.equal(events.length, 3+1, 'Three data events, plus the end event')
|
180
|
+
|
181
|
+
t.ok(events[0].elapsed < 10, 'Immediate emit first data event')
|
182
|
+
t.ok(events[1].elapsed >= 100 && events[1].elapsed < 125, 'About 100ms delay until the second event')
|
183
|
+
t.ok(events[2].elapsed - events[1].elapsed < 10, 'Immediate emit of subsequent event after resume')
|
184
|
+
t.equal(events[3], 'END', 'End event was fired')
|
185
|
+
|
186
|
+
t.end()
|
187
|
+
}
|
188
|
+
})
|
189
|
+
|
190
|
+
test('Continuous feed', function(t) {
|
191
|
+
for(var i = 0; i < 2; i++) {
|
192
|
+
var feed = new follow.Changes({'feed':'continuous'})
|
193
|
+
|
194
|
+
var data = []
|
195
|
+
feed.on('data', function(d) { data.push(d) })
|
196
|
+
feed.on('end', function() { data.push('END') })
|
197
|
+
|
198
|
+
var beats = 0
|
199
|
+
feed.on('heartbeat', function() { beats += 1 })
|
200
|
+
|
201
|
+
function encode(data) { return (i == 0) ? data : new Buffer(data) }
|
202
|
+
function write(data) { return function() { feed.write(encode(data)) } }
|
203
|
+
function end(data) { return function() { feed.end(encode(data)) } }
|
204
|
+
|
205
|
+
// This also tests whether the feed is compacting or tightening up the JSON.
|
206
|
+
t.doesNotThrow(write('{ }\n') , 'Empty object')
|
207
|
+
t.doesNotThrow(write('\n') , 'Heartbeat')
|
208
|
+
t.doesNotThrow(write('{ "foo" : "bar" }\n') , 'One object')
|
209
|
+
t.doesNotThrow(write('{"three":3}\n{ "four": 4}\n'), 'Two objects sent in one chunk')
|
210
|
+
t.doesNotThrow(write('') , 'Empty string')
|
211
|
+
t.doesNotThrow(write('\n') , 'Another heartbeat')
|
212
|
+
t.doesNotThrow(write('') , 'Another empty string')
|
213
|
+
t.doesNotThrow(write('{ "end" ') , 'Partial object 1/4')
|
214
|
+
t.doesNotThrow(write(':') , 'Partial object 2/4')
|
215
|
+
t.doesNotThrow(write('tru') , 'Partial object 3/4')
|
216
|
+
t.doesNotThrow(end('e}\n') , 'Partial object 4/4')
|
217
|
+
|
218
|
+
t.equal(data.length, 6, 'Five objects emitted, plus the end event')
|
219
|
+
t.equal(beats, 2, 'Two heartbeats emitted')
|
220
|
+
|
221
|
+
t.equal(data[0], '{}', 'First object emitted')
|
222
|
+
t.equal(data[1], '{"foo":"bar"}', 'Second object emitted')
|
223
|
+
t.equal(data[2], '{"three":3}', 'Third object emitted')
|
224
|
+
t.equal(data[3], '{"four":4}', 'Fourth object emitted')
|
225
|
+
t.equal(data[4], '{"end":true}', 'Fifth object emitted')
|
226
|
+
t.equal(data[5], 'END', 'End event fired')
|
227
|
+
}
|
228
|
+
|
229
|
+
t.end()
|
230
|
+
})
|
231
|
+
|
232
|
+
test('Continuous pause', function(t) {
|
233
|
+
var feed = new follow.Changes({'feed':'continuous'})
|
234
|
+
, all = [{'change':1}, {'second':'change'},{'#3':'change'}]
|
235
|
+
, start = new Date
|
236
|
+
|
237
|
+
var events = []
|
238
|
+
|
239
|
+
feed.on('end', function() {
|
240
|
+
t.equal(feed.readable, false, 'Feed is not readable after "end" event')
|
241
|
+
events.push('END')
|
242
|
+
})
|
243
|
+
|
244
|
+
feed.on('data', function(change) {
|
245
|
+
change = JSON.parse(change)
|
246
|
+
change.elapsed = new Date - start
|
247
|
+
events.push(change)
|
248
|
+
})
|
249
|
+
|
250
|
+
feed.once('data', function(data) {
|
251
|
+
t.equal(data, '{"change":1}', 'First data event was the first change')
|
252
|
+
t.equal(feed.readable, true, 'Feed is readable after first data event')
|
253
|
+
feed.pause()
|
254
|
+
t.equal(feed.readable, true, 'Feed is readable after pause()')
|
255
|
+
|
256
|
+
setTimeout(unpause, 100)
|
257
|
+
function unpause() {
|
258
|
+
t.equal(feed.readable, true, 'Feed is readable just before resume()')
|
259
|
+
feed.resume()
|
260
|
+
}
|
261
|
+
})
|
262
|
+
|
263
|
+
setTimeout(check_events, 150)
|
264
|
+
all.forEach(function(obj) {
|
265
|
+
feed.write(JSON.stringify(obj))
|
266
|
+
feed.write("\r\n")
|
267
|
+
})
|
268
|
+
feed.end()
|
269
|
+
|
270
|
+
function check_events() {
|
271
|
+
t.equal(events.length, 3+1, 'Three data events, plus the end event')
|
272
|
+
|
273
|
+
t.ok(events[0].elapsed < 10, 'Immediate emit first data event')
|
274
|
+
t.ok(events[1].elapsed >= 100 && events[1].elapsed < 125, 'About 100ms delay until the second event')
|
275
|
+
t.ok(events[2].elapsed - events[1].elapsed < 10, 'Immediate emit of subsequent event after resume')
|
276
|
+
t.equal(events[3], 'END', 'End event was fired')
|
277
|
+
|
278
|
+
t.end()
|
279
|
+
}
|
280
|
+
})
|
281
|
+
|
282
|
+
test('Feeds from couch', function(t) {
|
283
|
+
t.ok(couch.rtt(), 'RTT to couch is known')
|
284
|
+
|
285
|
+
var did = 0
|
286
|
+
function done() {
|
287
|
+
did += 1
|
288
|
+
if(did == 2)
|
289
|
+
t.end()
|
290
|
+
}
|
291
|
+
|
292
|
+
var types = [ 'longpoll', 'continuous' ]
|
293
|
+
types.forEach(function(type) {
|
294
|
+
var feed = new follow.Changes({'feed':type})
|
295
|
+
setTimeout(check_changes, couch.rtt() * 2)
|
296
|
+
|
297
|
+
var events = []
|
298
|
+
feed.on('data', function(data) { events.push(JSON.parse(data)) })
|
299
|
+
feed.on('end', function() { events.push('END') })
|
300
|
+
|
301
|
+
var uri = couch.DB + '/_changes?feed=' + type
|
302
|
+
var req = request({'uri':uri, 'onResponse':true}, on_response)
|
303
|
+
|
304
|
+
// Disconnect the continuous feed after a while.
|
305
|
+
if(type == 'continuous')
|
306
|
+
setTimeout(function() { feed.destroy() }, couch.rtt() * 1)
|
307
|
+
|
308
|
+
function on_response(er, res, body) {
|
309
|
+
t.false(er, 'No problem fetching '+type+' feed: ' + uri)
|
310
|
+
t.type(body, 'undefined', 'No data in '+type+' callback. This is an onResponse callback')
|
311
|
+
t.type(res.body, 'undefined', 'No response body in '+type+' callback. This is an onResponse callback')
|
312
|
+
t.ok(req.response, 'The request object has its '+type+' response by now')
|
313
|
+
|
314
|
+
req.pipe(feed)
|
315
|
+
|
316
|
+
t.equal(feed.statusCode, 200, 'Upon piping from request, the statusCode is set')
|
317
|
+
t.ok('content-type' in feed.headers, 'Upon piping from request, feed has headers set')
|
318
|
+
}
|
319
|
+
|
320
|
+
function check_changes() {
|
321
|
+
var expected_count = 3
|
322
|
+
if(type == 'longpoll')
|
323
|
+
expected_count += 1 // For the "end" event
|
324
|
+
|
325
|
+
t.equal(events.length, expected_count, 'Change event count for ' + type)
|
326
|
+
|
327
|
+
t.equal(events[0].seq, 1, 'First '+type+' update sequence id')
|
328
|
+
t.equal(events[1].seq, 2, 'Second '+type+' update sequence id')
|
329
|
+
t.equal(events[2].seq, 3, 'Third '+type+' update sequence id')
|
330
|
+
|
331
|
+
t.equal(good_id(events[0]), true, 'First '+type+' update is a good doc id: ' + events[0].id)
|
332
|
+
t.equal(good_id(events[1]), true, 'Second '+type+' update is a good doc id: ' + events[1].id)
|
333
|
+
t.equal(good_id(events[2]), true, 'Third '+type+' update is a good doc id: ' + events[2].id)
|
334
|
+
|
335
|
+
if(type == 'longpoll')
|
336
|
+
t.equal(events[3], 'END', 'End event fired for '+type)
|
337
|
+
else
|
338
|
+
t.type(events[3], 'undefined', 'No end event for a destroyed continuous feed')
|
339
|
+
|
340
|
+
done()
|
341
|
+
}
|
342
|
+
|
343
|
+
var good_ids = {'doc_first':true, 'doc_second':true, 'doc_third':true}
|
344
|
+
function good_id(event) {
|
345
|
+
var is_good = good_ids[event.id]
|
346
|
+
delete good_ids[event.id]
|
347
|
+
return is_good
|
348
|
+
}
|
349
|
+
})
|
350
|
+
})
|
351
|
+
|
352
|
+
test('Pausing and destroying a feed mid-stream', function(t) {
|
353
|
+
t.ok(couch.rtt(), 'RTT to couch is known')
|
354
|
+
var IMMEDIATE = 20
|
355
|
+
, FIRST_PAUSE = couch.rtt() * 8
|
356
|
+
, SECOND_PAUSE = couch.rtt() * 12
|
357
|
+
|
358
|
+
// To be really, really sure that backpressure goes all the way to couch, create more
|
359
|
+
// documents than could possibly be buffered. Linux and OSX seem to have a 16k MTU for
|
360
|
+
// the local interface, so a few hundred kb worth of document data should cover it.
|
361
|
+
couch.make_data(512 * 1024, check)
|
362
|
+
|
363
|
+
var types = ['longpoll', 'continuous']
|
364
|
+
function check(bulk_docs_count) {
|
365
|
+
var type = types.shift()
|
366
|
+
if(!type)
|
367
|
+
return t.end()
|
368
|
+
|
369
|
+
var feed = new follow.Changes
|
370
|
+
feed.feed = type
|
371
|
+
|
372
|
+
var destroys = 0
|
373
|
+
function destroy() {
|
374
|
+
destroys += 1
|
375
|
+
feed.destroy()
|
376
|
+
|
377
|
+
// Give one more RTT time for everything to wind down before checking how it all went.
|
378
|
+
if(destroys == 1)
|
379
|
+
setTimeout(check_events, couch.rtt())
|
380
|
+
}
|
381
|
+
|
382
|
+
var events = {'feed':[], 'http':[], 'request':[]}
|
383
|
+
, firsts = {'feed':null, 'http':null, 'request':null}
|
384
|
+
function ev(type, value) {
|
385
|
+
var now = new Date
|
386
|
+
firsts[type] = firsts[type] || now
|
387
|
+
events[type].push({'elapsed':now - firsts[type], 'at':now, 'val':value, 'before_destroy':(destroys == 0)})
|
388
|
+
}
|
389
|
+
|
390
|
+
feed.on('heartbeat', function() { ev('feed', 'heartbeat') })
|
391
|
+
feed.on('error', function(er) { ev('feed', er) })
|
392
|
+
feed.on('close', function() { ev('feed', 'close') })
|
393
|
+
feed.on('data', function(data) { ev('feed', data) })
|
394
|
+
feed.on('end', function() { ev('feed', 'end') })
|
395
|
+
|
396
|
+
var data_count = 0
|
397
|
+
feed.on('data', function() {
|
398
|
+
data_count += 1
|
399
|
+
if(data_count == 4) {
|
400
|
+
feed.pause()
|
401
|
+
setTimeout(function() { feed.resume() }, FIRST_PAUSE)
|
402
|
+
}
|
403
|
+
|
404
|
+
if(data_count == 7) {
|
405
|
+
feed.pause()
|
406
|
+
setTimeout(function() { feed.resume() }, SECOND_PAUSE - FIRST_PAUSE)
|
407
|
+
}
|
408
|
+
|
409
|
+
if(data_count >= 10)
|
410
|
+
destroy()
|
411
|
+
})
|
412
|
+
|
413
|
+
var uri = couch.DB + '/_changes?include_docs=true&feed=' + type
|
414
|
+
if(type == 'continuous')
|
415
|
+
uri += '&heartbeat=' + Math.floor(couch.rtt())
|
416
|
+
|
417
|
+
var req = request({'uri':uri, 'onResponse':feed_response})
|
418
|
+
req.on('error', function(er) { ev('request', er) })
|
419
|
+
req.on('close', function() { ev('request', 'close') })
|
420
|
+
req.on('data', function(d) { ev('request', d) })
|
421
|
+
req.on('end', function() { ev('request', 'end') })
|
422
|
+
req.pipe(feed)
|
423
|
+
|
424
|
+
function feed_response(er, res) {
|
425
|
+
if(er) throw er
|
426
|
+
|
427
|
+
res.on('error', function(er) { ev('http', er) })
|
428
|
+
res.on('close', function() { ev('http', 'close') })
|
429
|
+
res.on('data', function(d) { ev('http', d) })
|
430
|
+
res.on('end', function() { ev('http', 'end') })
|
431
|
+
|
432
|
+
t.equal(events.feed.length, 0, 'No feed events yet: ' + type)
|
433
|
+
t.equal(events.http.length, 0, 'No http events yet: ' + type)
|
434
|
+
t.equal(events.request.length, 0, 'No request events yet: ' + type)
|
435
|
+
}
|
436
|
+
|
437
|
+
function check_events() {
|
438
|
+
t.equal(destroys, 1, 'Only necessary to call destroy once: ' + type)
|
439
|
+
t.equal(events.feed.length, 10, 'Ten '+type+' data events fired')
|
440
|
+
if(events.feed.length != 10)
|
441
|
+
events.feed.forEach(function(e, i) {
|
442
|
+
console.error((i+1) + ') ' + util.inspect(e))
|
443
|
+
})
|
444
|
+
|
445
|
+
events.feed.forEach(function(event, i) {
|
446
|
+
var label = type + ' event #' + (i+1) + ' at ' + event.elapsed + ' ms'
|
447
|
+
|
448
|
+
t.type(event.val, 'string', label+' was a data string')
|
449
|
+
t.equal(event.before_destroy, true, label+' fired before the destroy')
|
450
|
+
|
451
|
+
var change = null
|
452
|
+
t.doesNotThrow(function() { change = JSON.parse(event.val) }, label+' was JSON: ' + type)
|
453
|
+
t.ok(change && change.seq > 0 && change.id, label+' was change data: ' + type)
|
454
|
+
|
455
|
+
// The first batch of events should have fired quickly (IMMEDIATE), then silence. Then another batch
|
456
|
+
// of events at the FIRST_PAUSE mark. Then more silence. Then a final batch at the SECOND_PAUSE mark.
|
457
|
+
if(i < 4)
|
458
|
+
t.ok(event.elapsed < IMMEDIATE, label+' was immediate (within '+IMMEDIATE+' ms)')
|
459
|
+
else if(i < 7)
|
460
|
+
t.ok(is_almost(event.elapsed, FIRST_PAUSE), label+' was after the first pause (about '+FIRST_PAUSE+' ms)')
|
461
|
+
else
|
462
|
+
t.ok(is_almost(event.elapsed, SECOND_PAUSE), label+' was after the second pause (about '+SECOND_PAUSE+' ms)')
|
463
|
+
})
|
464
|
+
|
465
|
+
if(type == 'continuous') {
|
466
|
+
t.ok(events.http.length >= 10, 'Should have at least ten '+type+' HTTP events')
|
467
|
+
t.ok(events.request.length >= 10, 'Should have at least ten '+type+' request events')
|
468
|
+
|
469
|
+
t.ok(events.http.length < 200, type+' HTTP events ('+events.http.length+') stop before 100')
|
470
|
+
t.ok(events.request.length < 200, type+' request events ('+events.request.length+') stop before 100')
|
471
|
+
|
472
|
+
var frac = events.http.length / bulk_docs_count
|
473
|
+
t.ok(frac < 0.10, 'Percent of http events received ('+frac.toFixed(1)+'%) is less than 10% of the data')
|
474
|
+
|
475
|
+
frac = events.request.length / bulk_docs_count
|
476
|
+
t.ok(frac < 0.10, type+' request events received ('+frac.toFixed(1)+'%) is less than 10% of the data')
|
477
|
+
}
|
478
|
+
|
479
|
+
return check(bulk_docs_count)
|
480
|
+
}
|
481
|
+
}
|
482
|
+
})
|
483
|
+
|
484
|
+
//
|
485
|
+
// Utilities
|
486
|
+
//
|
487
|
+
|
488
|
+
function is_almost(actual, expected) {
|
489
|
+
var tolerance = 0.10 // 10%
|
490
|
+
, diff = Math.abs(actual - expected)
|
491
|
+
, fraction = diff / expected
|
492
|
+
return fraction < tolerance
|
493
|
+
}
|