| // Licensed under the Apache License, Version 2.0 (the "License"); you may not |
| // use this file except in compliance with the License. You may obtain a copy of |
| // the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
| // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
| // License for the specific language governing permissions and limitations under |
| // the License. |
| couchTests.elixir = true; |
| |
| function jsonp(obj) { |
| T(jsonp_flag == 0); |
| T(obj.results.length == 1 && obj.last_seq == 1, "jsonp"); |
| jsonp_flag = 1; |
| } |
| |
| couchTests.changes = function(debug) { |
| return console.log('done in test/elixir/test/changes_test.exs and changes_async_test.exs'); |
| |
| var db; |
| if (debug) debugger; |
| |
| // poor man's browser detection |
| var is_safari = false; |
| if (typeof (navigator) == "undefined") { |
| is_safari = true; // For CouchHTTP based runners |
| } else if (navigator.userAgent.match(/AppleWebKit/)) { |
| is_safari = true; |
| } |
| |
| testChanges("live"); |
| testChanges("continuous"); |
| function testChanges(feed) { |
| var db_name = get_random_db_name(); |
| // (write-quorums help keep a consistent feed) |
| db = new CouchDB(db_name, {"X-Couch-Full-Commit":"true"}, {"w": 3}); |
| db.createDb(); |
| |
| var req = CouchDB.request("GET", "/" + db_name + "/_changes"); |
| var resp = JSON.parse(req.responseText); |
| |
| TEquals(0, resp.results.length, "db must be empty") |
| TEquals("0", resp.last_seq.substr(0, 1), "seq must start with 0") |
| var docFoo = {_id:"foo", bar:1}; |
| T(db.save(docFoo).ok); |
| T(db.ensureFullCommit().ok); |
| T(db.open(docFoo._id)._id == docFoo._id); |
| |
| retry_part(function(){ // avoid Heisenbugs |
| req = CouchDB.request("GET", "/" + db_name + "/_changes"); |
| var resp = JSON.parse(req.responseText); |
| TEquals("1", resp.last_seq.substr(0, 1), "seq must start with 1"); |
| T(resp.results.length == 1, "one doc db"); |
| T(resp.results[0].changes[0].rev == docFoo._rev); |
| }); |
| |
| // test with callback |
| // TODO: either allow jsonp in the default global config or implement a config chg mechanism analogouts 2 sebastianrothbucher:clustertest - or leave out |
| // run_on_modified_server( |
| // [{section: "httpd", |
| // key: "allow_jsonp", |
| // value: "true"}], |
| // function() { |
| // var xhr = CouchDB.request("GET", "/" + db_name + "/_changes?callback=jsonp"); |
| // T(xhr.status == 200); |
| // jsonp_flag = 0; |
| // eval(xhr.responseText); |
| // T(jsonp_flag == 1); |
| // }); |
| |
| // increase timeout to 100 to have enough time 2 assemble (seems like too little timeouts kill |
| req = CouchDB.request("GET", "/" + db_name + "/_changes?feed=" + feed + "&timeout=100"); |
| var lines = req.responseText.split("\n"); |
| T(JSON.parse(lines[0]).changes[0].rev == docFoo._rev); |
| // the sequence is not fully ordered and a complex structure now |
| T(JSON.parse(lines[1]).last_seq[0] == 1); |
| |
| var xhr; |
| |
| try { |
| xhr = CouchDB.newXhr(); |
| } catch (err) { |
| } |
| |
| // these will NEVER run as we're always in navigator == undefined |
| if (!is_safari && xhr) { |
| // Only test the continuous stuff if we have a real XHR object |
| // with real async support. |
| |
| // WebKit (last checked on nightly #47686) does fail on processing |
| // the async-request properly while javascript is executed. |
| |
| xhr.open("GET", CouchDB.proxyUrl("/" + db_name + "/_changes?feed=" + feed + "&timeout=500"), true); |
| xhr.send(""); |
| |
| var docBar = {_id:"bar", bar:1}; |
| db.save(docBar); |
| |
| var lines, change1, change2; |
| waitForSuccess(function() { |
| lines = xhr.responseText.split("\n"); |
| change1 = JSON.parse(lines[0]); |
| change2 = JSON.parse(lines[1]); |
| if (change2.seq != 2) { |
| throw "bad seq, try again"; |
| } |
| return true; |
| }, "bar-only"); |
| |
| T(change1.seq == 1); |
| T(change1.id == "foo"); |
| |
| T(change2.seq == 2); |
| T(change2.id == "bar"); |
| T(change2.changes[0].rev == docBar._rev); |
| |
| |
| var docBaz = {_id:"baz", baz:1}; |
| db.save(docBaz); |
| |
| var change3; |
| waitForSuccess(function() { |
| lines = xhr.responseText.split("\n"); |
| change3 = JSON.parse(lines[2]); |
| if (change3.seq != 3) { |
| throw "bad seq, try again"; |
| } |
| return true; |
| }); |
| |
| T(change3.seq == 3); |
| T(change3.id == "baz"); |
| T(change3.changes[0].rev == docBaz._rev); |
| |
| |
| xhr = CouchDB.newXhr(); |
| |
| //verify the heartbeat newlines are sent |
| xhr.open("GET", CouchDB.proxyUrl("/" + db_name + "/_changes?feed=" + feed + "&heartbeat=10&timeout=500"), true); |
| xhr.send(""); |
| |
| var str; |
| waitForSuccess(function() { |
| str = xhr.responseText; |
| if (str.charAt(str.length - 1) != "\n" || str.charAt(str.length - 2) != "\n") { |
| throw("keep waiting"); |
| } |
| return true; |
| }, "heartbeat"); |
| |
| T(str.charAt(str.length - 1) == "\n"); |
| T(str.charAt(str.length - 2) == "\n"); |
| |
| // otherwise we'll continue to receive heartbeats forever |
| xhr.abort(); |
| } |
| db.deleteDb(); |
| } |
| |
| // these will NEVER run as we're always in navigator == undefined |
| if (!is_safari && xhr) { |
| // test Server Sent Event (eventsource) |
| if (!!window.EventSource) { |
| var source = new EventSource( |
| "/" + db_name + "/_changes?feed=eventsource"); |
| var results = []; |
| var sourceListener = function(e) { |
| var data = JSON.parse(e.data); |
| results.push(data); |
| }; |
| |
| source.addEventListener('message', sourceListener , false); |
| |
| waitForSuccess(function() { |
| if (results.length != 3) { |
| throw "bad seq, try again"; |
| } |
| return true; |
| }); |
| |
| source.removeEventListener('message', sourceListener, false); |
| |
| T(results[0].seq == 1); |
| T(results[0].id == "foo"); |
| |
| T(results[1].seq == 2); |
| T(results[1].id == "bar"); |
| T(results[1].changes[0].rev == docBar._rev); |
| } |
| |
| // test that we receive EventSource heartbeat events |
| if (!!window.EventSource) { |
| var source = new EventSource( |
| "/" + db_name + "/_changes?feed=eventsource&heartbeat=10"); |
| |
| var count_heartbeats = 0; |
| source.addEventListener('heartbeat', function () { count_heartbeats = count_heartbeats + 1; } , false); |
| |
| waitForSuccess(function() { |
| if (count_heartbeats < 3) { |
| throw "keep waiting"; |
| } |
| return true; |
| }, "eventsource-heartbeat"); |
| |
| T(count_heartbeats >= 3); |
| source.close(); |
| } |
| |
| // test longpolling |
| xhr = CouchDB.newXhr(); |
| |
| xhr.open("GET", CouchDB.proxyUrl("/" + db_name + "/_changes?feed=longpoll"), true); |
| xhr.send(""); |
| |
| waitForSuccess(function() { |
| lines = xhr.responseText.split("\n"); |
| if (lines[5] != '"last_seq":3}') { |
| throw("still waiting"); |
| } |
| return true; |
| }, "last_seq"); |
| |
| xhr = CouchDB.newXhr(); |
| |
| xhr.open("GET", CouchDB.proxyUrl("/" + db_name + "/_changes?feed=longpoll&since=3"), true); |
| xhr.send(""); |
| |
| var docBarz = {_id:"barz", bar:1}; |
| db.save(docBarz); |
| |
| var parse_changes_line = function(line) { |
| if (line.charAt(line.length-1) == ",") { |
| var linetrimmed = line.substring(0, line.length-1); |
| } else { |
| var linetrimmed = line; |
| } |
| return JSON.parse(linetrimmed); |
| }; |
| |
| waitForSuccess(function() { |
| lines = xhr.responseText.split("\n"); |
| if (lines[3] != '"last_seq":4}') { |
| throw("still waiting"); |
| } |
| return true; |
| }, "change_lines"); |
| |
| var change = parse_changes_line(lines[1]); |
| T(change.seq == 4); |
| T(change.id == "barz"); |
| T(change.changes[0].rev == docBarz._rev); |
| T(lines[3]=='"last_seq":4}'); |
| |
| |
| // test since=now |
| xhr = CouchDB.newXhr(); |
| |
| xhr.open("GET", "/" + db_name + "/_changes?feed=longpoll&since=now", true); |
| xhr.send(""); |
| |
| var docBarz = {_id:"barzzzz", bar:1}; |
| db.save(docBarz); |
| |
| var parse_changes_line = function(line) { |
| if (line.charAt(line.length-1) == ",") { |
| var linetrimmed = line.substring(0, line.length-1); |
| } else { |
| var linetrimmed = line; |
| } |
| return JSON.parse(linetrimmed); |
| }; |
| |
| waitForSuccess(function() { |
| lines = xhr.responseText.split("\n"); |
| if (lines[3] != '"last_seq":5}') { |
| throw("still waiting"); |
| } |
| return true; |
| }, "change_lines"); |
| |
| var change = parse_changes_line(lines[1]); |
| T(change.seq == 5); |
| T(change.id == "barzzzz"); |
| T(change.changes[0].rev == docBarz._rev); |
| T(lines[3]=='"last_seq":5}'); |
| } |
| |
| db.deleteDb(); |
| // test on a new DB |
| var db_name = get_random_db_name(); |
| db = new CouchDB(db_name, {"X-Couch-Full-Commit":"true"}, {"w": 3}); |
| db.createDb(); |
| |
| // test the filtered changes |
| var ddoc = { |
| _id : "_design/changes_filter", |
| "filters" : { |
| "bop" : "function(doc, req) { return (doc.bop);}", |
| "dynamic" : stringFun(function(doc, req) { |
| var field = req.query.field; |
| return doc[field]; |
| }), |
| "userCtx" : stringFun(function(doc, req) { |
| return doc.user && (doc.user == req.userCtx.name); |
| }), |
| "conflicted" : "function(doc, req) { return (doc._conflicts);}" |
| }, |
| options : { |
| local_seq : true |
| }, |
| views : { |
| local_seq : { |
| map : "function(doc) {emit(doc._local_seq, null)}" |
| }, |
| blah: { |
| map : 'function(doc) {' + |
| ' if (doc._id == "blah") {' + |
| ' emit(null, null);' + |
| ' }' + |
| '}' |
| } |
| } |
| }; |
| |
| db.save(ddoc); |
| |
| var req = CouchDB.request("GET", "/" + db_name + "/_changes?filter=changes_filter/bop"); |
| var resp = JSON.parse(req.responseText); |
| T(resp.results.length == 0); |
| |
| var docres1 = db.save({"bop" : "foom"}); |
| T(docres1.ok); |
| var docres2 = db.save({"bop" : false}); |
| T(docres2.ok); |
| |
| var req = CouchDB.request("GET", "/" + db_name + "/_changes?filter=changes_filter/bop"); |
| var resp = JSON.parse(req.responseText); |
| var seqold = resp.results[0].seq; |
| T(resp.results.length == 1, "filtered/bop"); |
| T(resp.results[0].changes[0].rev == docres1.rev, "filtered/bop rev"); |
| // save and reload (substitute for all those parts that never run) |
| var chgdoc1 = db.open(docres1.id); |
| chgdoc1.newattr = "s/th new"; |
| docres1 = db.save(chgdoc1); |
| T(docres1.ok); |
| req = CouchDB.request("GET", "/" + db_name + "/_changes?filter=changes_filter/bop"); |
| resp = JSON.parse(req.responseText); |
| var seqchg = resp.results[0].seq; |
| T(resp.results.length == 1, "filtered/bop new"); |
| T(resp.results[0].changes[0].rev == docres1.rev, "filtered/bop rev new"); |
| T(seqold != seqchg, "filtered/bop new seq number"); |
| |
| req = CouchDB.request("GET", "/" + db_name + "/_changes?filter=changes_filter/dynamic&field=woox"); |
| resp = JSON.parse(req.responseText); |
| T(resp.results.length == 0); |
| |
| req = CouchDB.request("GET", "/" + db_name + "/_changes?filter=changes_filter/dynamic&field=bop"); |
| resp = JSON.parse(req.responseText); |
| T(resp.results.length == 1, "changes_filter/dynamic&field=bop"); |
| T(resp.results[0].changes[0].rev == docres1.rev, "filtered/dynamic&field=bop rev"); |
| |
| // these will NEVER run as we're always in navigator == undefined |
| if (!is_safari && xhr) { // full test requires parallel connections |
| // filter with longpoll |
| // longpoll filters full history when run without a since seq |
| xhr = CouchDB.newXhr(); |
| xhr.open("GET", CouchDB.proxyUrl("/" + db_name + "/_changes?feed=longpoll&filter=changes_filter/bop"), false); |
| xhr.send(""); |
| var resp = JSON.parse(xhr.responseText); |
| T(resp.last_seq == 8); |
| // longpoll waits until a matching change before returning |
| xhr = CouchDB.newXhr(); |
| xhr.open("GET", CouchDB.proxyUrl("/" + db_name + "/_changes?feed=longpoll&since=7&filter=changes_filter/bop"), true); |
| xhr.send(""); |
| db.save({"_id":"falsy", "bop" : ""}); // empty string is falsy |
| db.save({"_id":"bingo","bop" : "bingo"}); |
| |
| waitForSuccess(function() { |
| resp = JSON.parse(xhr.responseText); |
| return true; |
| }, "longpoll-since"); |
| |
| T(resp.last_seq == 10); |
| T(resp.results && resp.results.length > 0 && resp.results[0]["id"] == "bingo", "filter the correct update"); |
| xhr.abort(); |
| |
| var timeout = 500; |
| var last_seq = 11; |
| while (true) { |
| |
| // filter with continuous |
| xhr = CouchDB.newXhr(); |
| xhr.open("GET", CouchDB.proxyUrl("/" + db_name + "/_changes?feed=continuous&filter=changes_filter/bop&timeout="+timeout), true); |
| xhr.send(""); |
| |
| db.save({"_id":"rusty", "bop" : "plankton"}); |
| T(xhr.readyState != 4, "test client too slow"); |
| var rusty = db.open("rusty", {cache_bust : new Date()}); |
| T(rusty._id == "rusty"); |
| |
| waitForSuccess(function() { // throws an error after 5 seconds |
| if (xhr.readyState != 4) { |
| throw("still waiting"); |
| } |
| return true; |
| }, "continuous-rusty"); |
| lines = xhr.responseText.split("\n"); |
| var good = false; |
| try { |
| JSON.parse(lines[3]); |
| good = true; |
| } catch(e) { |
| } |
| if (good) { |
| T(JSON.parse(lines[1]).id == "bingo", lines[1]); |
| T(JSON.parse(lines[2]).id == "rusty", lines[2]); |
| T(JSON.parse(lines[3]).last_seq == last_seq, lines[3]); |
| break; |
| } else { |
| xhr.abort(); |
| db.deleteDoc(rusty); |
| timeout = timeout * 2; |
| last_seq = last_seq + 2; |
| } |
| } |
| } |
| // error conditions |
| |
| // non-existing design doc |
| var req = CouchDB.request("GET", |
| "/" + db_name + "/_changes?filter=nothingtosee/bop"); |
| TEquals(404, req.status, "should return 404 for non existant design doc"); |
| |
| // non-existing filter |
| var req = CouchDB.request("GET", |
| "/" + db_name + "/_changes?filter=changes_filter/movealong"); |
| TEquals(404, req.status, "should return 404 for non existant filter fun"); |
| |
| // both |
| var req = CouchDB.request("GET", |
| "/" + db_name + "/_changes?filter=nothingtosee/movealong"); |
| TEquals(404, req.status, |
| "should return 404 for non existant design doc and filter fun"); |
| |
| // changes get all_docs style with deleted docs |
| var doc = {a:1}; |
| db.save(doc); |
| db.deleteDoc(doc); |
| var req = CouchDB.request("GET", |
| "/" + db_name + "/_changes?filter=changes_filter/bop&style=all_docs"); |
| var resp = JSON.parse(req.responseText); |
| var expect = (!is_safari && xhr) ? 3: 1; |
| TEquals(expect, resp.results.length, "should return matching rows"); |
| |
| // test filter on view function (map) |
| // |
| T(db.save({"_id":"blah", "bop" : "plankton"}).ok); |
| var req = CouchDB.request("GET", "/" + db_name + "/_changes?filter=_view&view=changes_filter/blah"); |
| var resp = JSON.parse(req.responseText); |
| T(resp.results.length === 1); |
| T(resp.results[0].id === "blah"); |
| |
| |
| // test for userCtx |
| // TODO: either make part of global config, or allow 4 config changes - or leave out |
| /* |
| run_on_modified_server( |
| [{section: "httpd", |
| key: "authentication_handlers", |
| value: "{couch_httpd_auth, special_test_authentication_handler}"}, |
| {section:"httpd", |
| key: "WWW-Authenticate", |
| value: "X-Couch-Test-Auth"}], |
| |
| function() { |
| var authOpts = {"headers":{"WWW-Authenticate": "X-Couch-Test-Auth Chris Anderson:mp3"}}; |
| |
| var req = CouchDB.request("GET", "/_session", authOpts); |
| var resp = JSON.parse(req.responseText); |
| |
| T(db.save({"user" : "Noah Slater"}).ok); |
| var req = CouchDB.request("GET", "/" + db_name + "/_changes?filter=changes_filter/userCtx", authOpts); |
| var resp = JSON.parse(req.responseText); |
| T(resp.results.length == 0); |
| |
| var docResp = db.save({"user" : "Chris Anderson"}); |
| T(docResp.ok); |
| T(db.ensureFullCommit().ok); |
| req = CouchDB.request("GET", "/" + db_name + "/_changes?filter=changes_filter/userCtx", authOpts); |
| resp = JSON.parse(req.responseText); |
| T(resp.results.length == 1, "userCtx"); |
| T(resp.results[0].id == docResp.id); |
| } |
| ); |
| */ |
| |
| req = CouchDB.request("GET", "/" + db_name + "/_changes?limit=1"); |
| resp = JSON.parse(req.responseText); |
| TEquals(1, resp.results.length); |
| |
| //filter includes _conflicts |
| // TODO: all_or_nothing not yet in place |
| // var id = db.save({'food' : 'pizza'}).id; |
| // db.bulkSave([{_id: id, 'food' : 'pasta'}], {all_or_nothing:true}); |
| // |
| // req = CouchDB.request("GET", "/" + db_name + "/_changes?filter=changes_filter/conflicted"); |
| // resp = JSON.parse(req.responseText); |
| // T(resp.results.length == 1, "filter=changes_filter/conflicted"); |
| |
| // test with erlang filter function |
| // TODO: either make part of global config, or allow 4 config changes - or leave out |
| /* |
| run_on_modified_server([{ |
| section: "native_query_servers", |
| key: "erlang", |
| value: "{couch_native_process, start_link, []}" |
| }], function() { |
| var erl_ddoc = { |
| _id: "_design/erlang", |
| language: "erlang", |
| filters: { |
| foo: |
| 'fun({Doc}, Req) -> ' + |
| ' case couch_util:get_value(<<"value">>, Doc) of' + |
| ' undefined -> false;' + |
| ' Value -> (Value rem 2) =:= 0;' + |
| ' _ -> false' + |
| ' end ' + |
| 'end.' |
| } |
| }; |
| |
| db.deleteDb(); |
| db.createDb(); |
| T(db.save(erl_ddoc).ok); |
| |
| var req = CouchDB.request("GET", "/" + db_name + "/_changes?filter=erlang/foo"); |
| var resp = JSON.parse(req.responseText); |
| T(resp.results.length === 0); |
| |
| T(db.save({_id: "doc1", value : 1}).ok); |
| T(db.save({_id: "doc2", value : 2}).ok); |
| T(db.save({_id: "doc3", value : 3}).ok); |
| T(db.save({_id: "doc4", value : 4}).ok); |
| |
| var req = CouchDB.request("GET", "/" + db_name + "/_changes?filter=erlang/foo"); |
| var resp = JSON.parse(req.responseText); |
| T(resp.results.length === 2); |
| T(resp.results[0].id === "doc2"); |
| T(resp.results[1].id === "doc4"); |
| |
| // test filtering on docids |
| // |
| |
| var options = { |
| headers: {"Content-Type": "application/json"}, |
| body: JSON.stringify({"doc_ids": ["something", "anotherthing", "andmore"]}) |
| }; |
| |
| var req = CouchDB.request("POST", "/" + db_name + "/_changes?filter=_doc_ids", options); |
| var resp = JSON.parse(req.responseText); |
| T(resp.results.length === 0); |
| |
| T(db.save({"_id":"something", "bop" : "plankton"}).ok); |
| var req = CouchDB.request("POST", "/" + db_name + "/_changes?filter=_doc_ids", options); |
| var resp = JSON.parse(req.responseText); |
| T(resp.results.length === 1); |
| T(resp.results[0].id === "something"); |
| |
| T(db.save({"_id":"anotherthing", "bop" : "plankton"}).ok); |
| var req = CouchDB.request("POST", "/" + db_name + "/_changes?filter=_doc_ids", options); |
| var resp = JSON.parse(req.responseText); |
| T(resp.results.length === 2); |
| T(resp.results[0].id === "something"); |
| T(resp.results[1].id === "anotherthing"); |
| |
| var docids = JSON.stringify(["something", "anotherthing", "andmore"]), |
| req = CouchDB.request("GET", "/" + db_name + "/_changes?filter=_doc_ids&doc_ids="+docids, options); |
| var resp = JSON.parse(req.responseText); |
| T(resp.results.length === 2); |
| T(resp.results[0].id === "something"); |
| T(resp.results[1].id === "anotherthing"); |
| |
| var req = CouchDB.request("GET", "/" + db_name + "/_changes?filter=_design"); |
| var resp = JSON.parse(req.responseText); |
| T(resp.results.length === 1); |
| T(resp.results[0].id === "_design/erlang"); |
| |
| |
| if (!is_safari && xhr) { |
| // filter docids with continuous |
| xhr = CouchDB.newXhr(); |
| xhr.open("POST", CouchDB.proxyUrl("/" + db_name + "/_changes?feed=continuous&timeout=500&since=7&filter=_doc_ids"), true); |
| xhr.setRequestHeader("Content-Type", "application/json"); |
| |
| xhr.send(options.body); |
| |
| T(db.save({"_id":"andmore", "bop" : "plankton"}).ok); |
| |
| waitForSuccess(function() { |
| if (xhr.readyState != 4) { |
| throw("still waiting"); |
| } |
| return true; |
| }, "andmore-only"); |
| |
| var line = JSON.parse(xhr.responseText.split("\n")[0]); |
| T(line.seq == 8); |
| T(line.id == "andmore"); |
| } |
| }); |
| */ |
| |
| db.deleteDb(); |
| // COUCHDB-1037 - empty result for ?limit=1&filter=foo/bar in some cases |
| // test w/ new temp DB |
| db_name = get_random_db_name(); |
| db = new CouchDB(db_name, {"X-Couch-Full-Commit":"true"}, {"w": 3}); |
| T(db.createDb()); |
| |
| ddoc = { |
| _id: "_design/testdocs", |
| filters: { |
| testdocsonly: (function(doc, req) { |
| return (typeof doc.integer === "number"); |
| }).toString() |
| } |
| }; |
| T(db.save(ddoc)); |
| |
| ddoc = { |
| _id: "_design/foobar", |
| foo: "bar" |
| }; |
| T(db.save(ddoc)); |
| |
| db.bulkSave(makeDocs(0, 5)); |
| |
| // for n>1 you can't be sure all docs are there immediately - so either stick w/ -n 1 or implement check-wait-check or use the quorum (for now, the latter seems 2 suffice) |
| |
| req = CouchDB.request("GET", "/" + db.name + "/_changes"); |
| resp = JSON.parse(req.responseText); |
| // you can't know wether 7 is the last seq as you don't know how many collapse into one number |
| //TEquals(7, resp.last_seq); |
| TEquals(7, resp.results.length); |
| |
| req = CouchDB.request( |
| "GET", "/"+ db.name + "/_changes?limit=1&filter=testdocs/testdocsonly"); |
| resp = JSON.parse(req.responseText); |
| // (seq as before) |
| //TEquals(3, resp.last_seq); |
| TEquals(1, resp.results.length); |
| // also, we can't guarantee ordering |
| T(resp.results[0].id.match("[0-5]")); |
| |
| req = CouchDB.request( |
| "GET", "/" + db.name + "/_changes?limit=2&filter=testdocs/testdocsonly"); |
| resp = JSON.parse(req.responseText); |
| // (seq as before) |
| //TEquals(4, resp.last_seq); |
| TEquals(2, resp.results.length); |
| // also, we can't guarantee ordering |
| T(resp.results[0].id.match("[0-5]")); |
| T(resp.results[1].id.match("[0-5]")); |
| |
| // TODO: either use local port for stats (and aggregate when n>1) or leave out |
| // TEquals(0, CouchDB.requestStats(['couchdb', 'httpd', 'clients_requesting_changes'], true).value); |
| // CouchDB.request("GET", "/" + db.name + "/_changes"); |
| // TEquals(0, CouchDB.requestStats(['couchdb', 'httpd', 'clients_requesting_changes'], true).value); |
| |
| db.deleteDb(); |
| // COUCHDB-1256 |
| // test w/ new temp DB |
| db_name = get_random_db_name(); |
| db = new CouchDB(db_name, {"X-Couch-Full-Commit":"true"}, {"w": 3}); |
| T(db.createDb()); |
| |
| T(db.save({"_id":"foo", "a" : 123}).ok); |
| T(db.save({"_id":"bar", "a" : 456}).ok); |
| |
| options = { |
| headers: {"Content-Type": "application/json"}, |
| body: JSON.stringify({"_rev":"1-cc609831f0ca66e8cd3d4c1e0d98108a", "a":456}) |
| }; |
| req = CouchDB.request("PUT", "/" + db.name + "/foo?new_edits=false", options); |
| |
| req = CouchDB.request("GET", "/" + db.name + "/_changes?style=all_docs"); |
| resp = JSON.parse(req.responseText); |
| |
| // (seq as before) |
| //TEquals(3, resp.last_seq); |
| TEquals(2, resp.results.length); |
| |
| // we can no longer pass a number into 'since' - but we have the 2nd last above - so we can use it (puh!) |
| req = CouchDB.request("GET", "/" + db.name + "/_changes?style=all_docs&since=" + encodeURIComponent(resp.results[0].seq)); |
| resp = JSON.parse(req.responseText); |
| |
| // (seq as before) |
| //TEquals(3, resp.last_seq); |
| TEquals(1, resp.results.length); |
| // TEquals(2, resp.results[0].changes.length); |
| |
| db.deleteDb(); |
| // COUCHDB-1852 |
| // test w/ new temp DB |
| db_name = get_random_db_name(); |
| db = new CouchDB(db_name, {"X-Couch-Full-Commit":"true"}, {"w": 3}); |
| T(db.createDb()); |
| |
| // create 4 documents... this assumes the update sequnce will start from 0 and then do sth in the cluster |
| db.save({"bop" : "foom"}); |
| db.save({"bop" : "foom"}); |
| db.save({"bop" : "foom"}); |
| db.save({"bop" : "foom"}); |
| // because of clustering, we need the 2nd entry as since value |
| req = CouchDB.request("GET", "/" + db_name + "/_changes"); |
| |
| // simulate an EventSource request with a Last-Event-ID header |
| // increase timeout to 100 to have enough time 2 assemble (seems like too little timeouts kill |
| req = CouchDB.request("GET", "/" + db_name + "/_changes?feed=eventsource&timeout=100&since=0", |
| {"headers": {"Accept": "text/event-stream", "Last-Event-ID": JSON.parse(req.responseText).results[1].seq}}); |
| |
| // "parse" the eventsource response and collect only the "id: ..." lines |
| var changes = req.responseText.split('\n') |
| .map(function (el) { |
| return el.split(":").map(function (el) { return el.trim()}); |
| }) |
| .filter(function (el) { return (el[0] === "id"); }) |
| |
| // make sure we only got 2 changes, and they are update_seq=3 and update_seq=4 |
| T(changes.length === 2); |
| // seq is different now |
| //T(changes[0][1] === "3"); |
| //T(changes[1][1] === "4"); |
| |
| db.deleteDb(); |
| // COUCHDB-1923 |
| // test w/ new temp DB |
| db_name = get_random_db_name(); |
| db = new CouchDB(db_name, {"X-Couch-Full-Commit":"true"}, {"w": 3}); |
| T(db.createDb()); |
| |
| var attachmentData = "VGhpcyBpcyBhIGJhc2U2NCBlbmNvZGVkIHRleHQ="; |
| |
| db.bulkSave(makeDocs(20, 30, { |
| _attachments:{ |
| "foo.txt": { |
| content_type:"text/plain", |
| data: attachmentData |
| }, |
| "bar.txt": { |
| content_type:"text/plain", |
| data: attachmentData |
| } |
| } |
| })); |
| |
| var mapFunction = function(doc) { |
| var count = 0; |
| |
| for(var idx in doc._attachments) { |
| count = count + 1; |
| } |
| |
| emit(parseInt(doc._id), count); |
| }; |
| |
| var req = CouchDB.request("GET", "/" + db_name + "/_changes?include_docs=true"); |
| var resp = JSON.parse(req.responseText); |
| |
| T(resp.results.length == 10); |
| T(resp.results[0].doc._attachments['foo.txt'].stub === true); |
| T(resp.results[0].doc._attachments['foo.txt'].data === undefined); |
| T(resp.results[0].doc._attachments['foo.txt'].encoding === undefined); |
| T(resp.results[0].doc._attachments['foo.txt'].encoded_length === undefined); |
| T(resp.results[0].doc._attachments['bar.txt'].stub === true); |
| T(resp.results[0].doc._attachments['bar.txt'].data === undefined); |
| T(resp.results[0].doc._attachments['bar.txt'].encoding === undefined); |
| T(resp.results[0].doc._attachments['bar.txt'].encoded_length === undefined); |
| |
| var req = CouchDB.request("GET", "/" + db_name + "/_changes?include_docs=true&attachments=true"); |
| var resp = JSON.parse(req.responseText); |
| |
| T(resp.results.length == 10); |
| T(resp.results[0].doc._attachments['foo.txt'].stub === undefined); |
| T(resp.results[0].doc._attachments['foo.txt'].data === attachmentData); |
| T(resp.results[0].doc._attachments['foo.txt'].encoding === undefined); |
| T(resp.results[0].doc._attachments['foo.txt'].encoded_length === undefined); |
| T(resp.results[0].doc._attachments['bar.txt'].stub === undefined); |
| T(resp.results[0].doc._attachments['bar.txt'].data == attachmentData); |
| T(resp.results[0].doc._attachments['bar.txt'].encoding === undefined); |
| T(resp.results[0].doc._attachments['bar.txt'].encoded_length === undefined); |
| |
| var req = CouchDB.request("GET", "/" + db_name + "/_changes?include_docs=true&att_encoding_info=true"); |
| var resp = JSON.parse(req.responseText); |
| |
| T(resp.results.length == 10); |
| T(resp.results[0].doc._attachments['foo.txt'].stub === true); |
| T(resp.results[0].doc._attachments['foo.txt'].data === undefined); |
| T(resp.results[0].doc._attachments['foo.txt'].encoding === "gzip"); |
| T(resp.results[0].doc._attachments['foo.txt'].encoded_length === 47); |
| T(resp.results[0].doc._attachments['bar.txt'].stub === true); |
| T(resp.results[0].doc._attachments['bar.txt'].data === undefined); |
| T(resp.results[0].doc._attachments['bar.txt'].encoding === "gzip"); |
| T(resp.results[0].doc._attachments['bar.txt'].encoded_length === 47); |
| |
| db.deleteDb(); |
| }; |