vulcan 0.6.1 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|
+
}
|