(function () { var thisfun = function(loader) { var JIO = loader.JIO; // localStorage cleanup var k; for (k in localStorage) { if (/^jio\//.test(k)) { localStorage.removeItem(k); } } delete k; //// Tools var empty_fun = function (){}, contains = function (array,content) { var i; if (typeof array !== 'object') { return undefined; } for (i = 0; i < array.length || 0; i+= 1) { if (array[i] === content) { return true; } } return false; }, clone = function (obj) { var tmp = JSON.stringify(obj); if (tmp !== undefined) { return JSON.parse(tmp); } return tmp; }, // generates a revision hash from document metadata, revision history // and the deleted_flag generateRevisionHash = function (doc, revisions, deleted_flag) { var string; doc = clone(doc); delete doc._rev; delete doc._revs; delete doc._revs_info; string = JSON.stringify(doc) + JSON.stringify(revisions) + JSON.stringify(deleted_flag? true: false); return hex_sha256(string); }, // localStorage wrapper localstorage = { clear: function () { return localStorage.clear(); }, getItem: function (item) { var value = localStorage.getItem(item); return value === null? null: JSON.parse(value); }, setItem: function (item,value) { return localStorage.setItem(item,JSON.stringify (value)); }, removeItem: function (item) { return localStorage.removeItem(item); } }, cleanUpLocalStorage = function(){ var k, storageObject = localstorage.getAll(); for (k in storageObject) { var splitk = k.split('/'); if ( splitk[0] === 'jio' ) { localstorage.removeItem(k); } } var d = document.createElement ('div'); d.setAttribute('id','log'); document.querySelector ('body').appendChild(d); // remove everything localStorage.clear(); }, base_tick = 30000, basicTestFunctionGenerator = function(o,res,value,message) { return function(err,val) { var jobstatus = (err?'fail':'done'); switch (res) { case 'status': err = err || {}; val = err.status; break; case 'jobstatus': val = jobstatus; break; case 'value': val = err || val; break; default: ok(false, "Unknown case " + res); } deepEqual (val,value,message); }; }, /** * Prepare a specific test for jio and create a spy. * It creates a function [function_name] in [obj] which can be use as a * jio callback. To prepare the test, we need to know what kind of return * value you want -> [result_type]: * - "status": [value] is compared with err.status, the error code * - "jobstatus": [value] check if the request is "fail" or "done" * - "value": [value] is compared to the response * @method basicSpyFunction * @param {object} obj The object to work with * @param {string} result_type The result type * @param {object} value The value to be compared * @param {string} message The test message * @param {string} function_name The callback name */ basicSpyFunction = function(obj, result_type, value, message, function_name) { function_name = function_name || 'f'; obj[function_name] = basicTestFunctionGenerator(obj, result_type, value, message); obj.t.spy(obj, function_name); }, /** * Advances in time and execute the test previously prepared. * The default function to test is "f" in [obj]. * @method basicTickFunction * @param {object} obj The object to work with * @param {number} tick The time to advance in ms (optional) * @param {function_name} function_name The callback to test (optional) */ basicTickFunction = function (obj) { var tick, fun, i = 1; tick = 10000; fun = "f"; if (typeof arguments[i] === 'number') { tick = arguments[i]; i++; } if (typeof arguments[i] === 'string') { fun = arguments[i]; i++; } obj.clock.tick(tick); if (!obj[fun].calledOnce) { if (obj[fun].called) { ok(false, 'too much results (obj.' + fun +')'); } else { ok(false, 'no response (obj.' + fun +')'); } } }, getXML = function (url) { var xml = $.ajax({url:url, async:false}); return xml.responseText; }, objectifyDocumentArray = function (array) { var obj = {}, k; for (k = 0; k < array.length; k += 1) { obj[array[k]._id] = array[k]; } return obj; }, getLastJob = function (id) { return (localstorage.getItem("jio/job_array/"+id) || [undefined]).pop(); }, generateTools = function (test_namespace) { var o = {}; o.t = test_namespace; o.server = o.t.sandbox.server; o.clock = o.t.sandbox.clock; o.clock.tick(base_tick); o.spy = basicSpyFunction; o.tick = basicTickFunction; // test methods o.testLastJobLabel = function (label, mess) { var lastjob = getLastJob(o.jio.getId()); if (lastjob) { deepEqual(lastjob.command.label, label, mess); } else { deepEqual("No job on the queue", "Job with label: "+label, mess); } }; o.testLastJobId = function (id, mess) { var lastjob = getLastJob(o.jio.getId()); if (lastjob) { deepEqual(lastjob.id, id, mess); } else { deepEqual("No job on the queue", "Job with id: "+id, mess); } }; o.testLastJobWaitForTime = function (mess) { var lastjob = getLastJob(o.jio.getId()); if (lastjob) { ok(lastjob.status.waitfortime > 0, mess); } else { deepEqual("No job on the queue", "Job waiting for time", mess); } }; o.testLastJobWaitForJob = function (job_id_array, mess) { var lastjob = getLastJob(o.jio.getId()); if (lastjob) { deepEqual(lastjob.status.waitforjob, job_id_array, mess); } else { deepEqual( "No job on the queue", "Job waiting for: " + JSON.stringify (job_id_array), mess ); } }; // wait method // NOTED: not sure I understood this correctly o.waitUntilAJobExists = function (timeout) { var cpt = 0, job = false; while (true) { if (getLastJob(o.jio.getId()) !== undefined) { job = true; break; } if (cpt >= timeout) { break; } o.clock.tick(25); cpt += 25; } ok(job, "Waited until job was created"); }; o.waitUntilLastJobIs = function (state) { while (true) { if (getLastJob(o.jio.getId()) === undefined) { ok(false, "No job have state: " + state); break; } if (getLastJob(o.jio.getId()).status.label === state) { break; } o.clock.tick(25); } }; o.constructFakeServerUrl = function(type, path) { switch (type) { case "dav": return 'https:\\/\\/ca-davstorage:8080\\/' + path + '(\\?.*|$)'; default: return path; } }; o.addFakeServerResponse = function (type, method, path, status, response) { var url = new RegExp(o.constructFakeServerUrl(type, path) ); o.server.respondWith(method, url, [status, { "Content-Type": 'application/xml' }, response] ); }; o.sortArrayById = function(field, reverse, primer){ var key = function (x) {return primer ? primer(x[field]) : x[field]}; return function (a,b) { var A = key(a), B = key(b); return ( (A < B) ? -1 : ((A > B) ? 1 : 0) ) * [-1,1][+!!reverse]; } }; return o; }, //// end tools //// test function isUuid = function (uuid) { var x = "[0-9a-fA-F]{4}"; if (typeof uuid !== "string" ) { return false; } return uuid.match("^"+x+x+"-"+x+"-"+x+"-"+x+"-"+x+x+x+"$") === null? false: true; }; //// QUnit Tests //// module('Complex Queries'); test('Empty Query', function () { var doc_list = [ {"identifier": "a"}, {"identifier": ["b", "c"]} ]; complex_queries.QueryFactory.create('').exec(doc_list); deepEqual(doc_list, [ {"identifier": "a"}, {"identifier": ["b", "c"]} ], 'Nothing done on the list'); }); test('Simple Query', function () { var doc_list = [ {"identifier": "a"}, {"identifier": ["b", "c"]} ]; complex_queries.QueryFactory.create('identifier: "a"').exec(doc_list); deepEqual(doc_list, [ {"identifier": "a"}, ], 'Document with several identifier should be removed'); doc_list = [ {"identifier": "a"}, {"identifier": ["a", "b"]} ]; complex_queries.QueryFactory.create('identifier: "a"').exec(doc_list); deepEqual(doc_list, [ {"identifier": "a"}, {"identifier": ["a", "b"]} ], 'Document with several identifier should be kept'); }); test('Complex Query', function () { var doc_list = [ {"identifier": "a"}, {"identifier": ["b", "c"]} ]; complex_queries.QueryFactory.create( 'identifier: "b" AND identifier: "c"' ).exec(doc_list); deepEqual(doc_list, [ {"identifier": ["b", "c"]} ], 'Document with only one identifier should be removed'); doc_list = [ {"identifier": "a"}, {"identifier": ["b", "c"]} ]; complex_queries.QueryFactory.create( 'identifier: "a" OR identifier: "c"' ).exec(doc_list); deepEqual(doc_list, [ {"identifier": "a"}, {"identifier": ["b", "c"]} ], 'All document matches'); doc_list = [ {"identifier": "a", "title": "o"}, {"identifier": ["b", "c"]} ]; complex_queries.QueryFactory.create( '(identifier: "a" OR identifier: "b") AND title: "o"' ).exec(doc_list); deepEqual(doc_list, [ {"identifier": "a", "title": "o"} ], 'Only first document should be kept'); }); test('Wildcard Character', function () { var doc_list = [ {"identifier": "a"}, {"identifier": "a%"}, {"identifier": ["ab", "b"]} ]; complex_queries.QueryFactory.create('identifier: "a%"').exec(doc_list, { // "wildcard_character": "%" // default }); deepEqual(doc_list, [ {"identifier": "a"}, {"identifier": "a%"}, {"identifier": ["ab", "b"]} ], 'All documents should be kept'); doc_list = [ {"identifier": "a"}, {"identifier": "a%"}, {"identifier": ["ab", "b"]} ]; complex_queries.QueryFactory.create('identifier: "a%"').exec(doc_list, { "wildcard_character": null }); deepEqual(doc_list, [ {"identifier": "a%"} ], 'Document "a%" should be kept'); doc_list = [ {"identifier": "a"}, {"identifier": "a%"}, {"identifier": ["ab", "b"]} ]; complex_queries.QueryFactory.create('identifier: "b"').exec(doc_list, { "wildcard_character": "b" }); deepEqual(doc_list, [ {"identifier": "a"}, {"identifier": "a%"}, {"identifier": ["ab", "b"]} ], 'All documents should be kept'); }); test("Additional Filters", function () { var doc_list = [ {"identifier": "b", "title": "e"}, {"identifier": "a", "title": "f"}, {"identifier": "b", "title": "d"} ]; complex_queries.QueryFactory.create('').exec(doc_list, { "select_list": ["title"], "limit": [2, 1], "sort_on": [["identifier", "ascending"], ["title", "descending"]] }); deepEqual(doc_list, [ {"title": "d"} ], 'The first document should be kept'); }); module ('Jio Global tests'); test ( "Jio simple methods", function () { // Test Jio simple methods // It checks if we can create several instance of jio at the same // time. Checks if they don't overlap informations, if they are // started and stopped correctly and if they are ready when they // have to be ready. var o = generateTools(this); o.jio = JIO.newJio(); ok ( o.jio, 'a new jio -> 1'); o.jio2 = JIO.newJio(); ok ( o.jio2, 'another new jio -> 2'); JIO.addStorageType('qunit', empty_fun); ok ( o.jio2.getId() !== o.jio.getId(), '1 and 2 must be different'); o.jio.stop(); o.jio2.stop(); }); // test ( 'Jio Publish/Sububscribe/Unsubscribe methods', function () { // // Test the Publisher, Subscriber of a single jio. // // It is just testing if these function are working correctly. // // The test publishes an event, waits a little, and check if the // // event has been received by the callback of the previous // // subscribe. Then, the test unsubscribe the callback function from // // the event, and publish the same event. If it receives the event, // // the unsubscribe method is not working correctly. // var o = {}; // o.jio = JIO.newJio(); // var spy1 = this.spy(); // // Subscribe the pubsub_test event. // o.callback = o.jio.subscribe('pubsub_test',spy1); // // And publish the event. // o.jio.publish('pubsub_test'); // ok (spy1.calledOnce, 'subscribing & publishing, event called once'); // o.jio.unsubscribe('pubsub_test',spy1); // o.jio.publish('pubsub_test'); // ok (spy1.calledOnce, 'unsubscribing, same event not called twice'); // o.jio.stop(); // }); module ( "Jio Dummy Storages" ); test ("All requests ok", function () { // Tests the request methods and the response with dummy storages var o = generateTools(this); // All Ok Dummy Storage o.jio = JIO.newJio({"type": "dummyallok"}); // post empty document, some storage can create there own id (like couchdb // generates uuid). In this case, the dummy storage write an undefined id. o.spy(o, "value", {"ok": true, "id": undefined}, "Post document with empty id"); o.jio.post({}, o.f); o.tick(o); // post non empty document o.spy(o, "value", {"ok": true, "id": "file"}, "Post non empty document"); o.jio.post({"_id": "file", "title": "myFile"}, o.f); o.tick(o); // put without id // error 20 -> document id required o.spy(o, "status", 20, "Put document with empty id"); o.jio.put({}, o.f); o.tick(o); // put non empty document o.spy(o, "value", {"ok": true, "id": "file"}, "Put non empty document"); o.jio.put({"_id": "file", "title": "myFile"}, o.f); o.tick(o); // put an attachment without attachment id // error 22 -> attachment id required o.spy(o, "status", 22, "Put attachment without id"); o.jio.putAttachment({ "_id": "file", "_data": "0123456789", "_mimetype": "text/plain" }, o.f); o.tick(o); // put an attachment o.spy(o, "value", {"ok": true, "id": "file", "attachment": "attmt"}, "Put attachment"); o.jio.putAttachment({ "_id": "file", "_attachment": "attmt", "_data": "0123456789", "_mimetype": "text/plain" }, o.f); o.tick(o); // get document o.spy(o, "value", {"_id": "file", "title": "get_title"}, "Get document"); o.jio.get({"_id": "file"}, o.f); o.tick(o); // get attachment o.spy(o, "value", "0123456789", "Get attachment"); o.jio.getAttachment({"_id": "file", "_attachment": "attmt"}, o.f); o.tick(o); // remove document o.spy(o, "value", {"ok": true, "id": "file"}, "Remove document"); o.jio.remove({"_id": "file"}, o.f); o.tick(o); // remove attachment o.spy(o, "value", {"ok": true, "id": "file", "attachment": "attmt"}, "Remove attachment"); o.jio.removeAttachment({"_id": "file", "_attachment": "attmt"}, o.f); o.tick(o); // alldocs // error 405 -> Method not allowed o.spy(o, "status", 405, "AllDocs fail"); o.jio.allDocs(o.f); o.tick(o); o.jio.stop(); }); test ("All requests fail", function () { // Tests the request methods and the err object with dummy storages var o = generateTools(this); // All Ok Dummy Storage o.jio = JIO.newJio({"type": "dummyallfail"}); // post empty document // error 0 -> unknown o.spy(o, "status", 0, "Post document with empty id"); o.jio.post({}, o.f); o.tick(o); // test if the job still exists if (getLastJob(o.jio.getId()) !== undefined) { ok(false, "The job is not removed from the job queue"); } // post non empty document o.spy(o, "status", 0, "Post non empty document"); o.jio.post({"_id": "file", "title": "myFile"}, o.f); o.tick(o); // put without id // error 20 -> document id required o.spy(o, "status", 20, "Put document with empty id"); o.jio.put({}, o.f); o.tick(o); // put non empty document o.spy(o, "status", 0, "Put non empty document"); o.jio.put({"_id": "file", "title": "myFile"}, o.f); o.tick(o); // put an attachment without attachment id // error 22 -> attachment id required o.spy(o, "status", 22, "Put attachment without id"); o.jio.putAttachment({ "_id": "file", "_data": "0123456789", "_mimetype": "text/plain" }, o.f); o.tick(o); // put an attachment o.spy(o, "status", 0, "Put attachment"); o.jio.putAttachment({ "_id": "file", "_attachment": "attmt", "_data": "0123456789", "_mimetype": "text/plain" }, o.f); o.tick(o); // get document o.spy(o, "status", 0, "Get document"); o.jio.get({"_id": "file"}, o.f); o.tick(o); // get attachment o.spy(o, "status", 0, "Get attachment"); o.jio.get({"_id": "file", "_attachment": "attmt"}, o.f); o.tick(o); // remove document o.spy(o, "status", 0, "Remove document"); o.jio.remove({"_id": "file"}, o.f); o.tick(o); // remove attachment o.spy(o, "status", 0, "Remove attachment"); o.jio.remove({"_id": "file", "_attachment": "attmt"}, o.f); o.tick(o); // alldocs // error 405 -> Method not allowed o.spy(o, "status", 405, "AllDocs fail"); o.jio.allDocs(o.f); o.tick(o); o.jio.stop(); }); test ("All document not found", function () { // Tests the request methods without document var o = generateTools(this); // All Ok Dummy Storage o.jio = JIO.newJio({"type": "dummyallnotfound"}); // post document o.spy(o, "value", {"ok": true, "id": "file"}, "Post document"); o.jio.post({"_id": "file", "title": "myFile"}, o.f); o.tick(o); // put document o.spy(o, "value", {"ok": true, "id": "file"}, "Put document"); o.jio.put({"_id": "file", "title": "myFile"}, o.f); o.tick(o); // put an attachment without attachment id // error 22 -> attachment id required o.spy(o, "status", 22, "Put attachment without id"); o.jio.putAttachment({ "_id": "file", "_data": "0123456789", "_mimetype": "text/plain" }, o.f); o.tick(o); // put an attachment o.spy(o, "value", {"ok": true, "id": "file", "attachment": "attmt"}, "Put attachment"); o.jio.putAttachment({ "_id": "file", "_attachment": "attmt", "_data": "0123456789", "_mimetype": "text/plain" }, o.f); o.tick(o); // get document o.spy(o, "status", 404, "Get document"); o.jio.get({"_id": "file"}, o.f); o.tick(o); // get attachment o.spy(o, "status", 404, "Get attachment"); o.jio.get({"_id": "file/attmt"}, o.f); o.tick(o); // remove document o.spy(o, "status", 404, "Remove document"); o.jio.remove({"_id": "file"}, o.f); o.tick(o); // remove attachment o.spy(o, "status", 404, "Remove attachment"); o.jio.removeAttachment({"_id": "file", "_attachment": "attmt"}, o.f); o.tick(o); o.jio.stop(); }); test ("All document found", function () { // Tests the request methods with document var o = generateTools(this); // All Ok Dummy Storage o.jio = JIO.newJio({"type": "dummyallfound"}); // post non empty document o.spy(o, "status", 409, "Post document"); o.jio.post({"_id": "file", "title": "myFile"}, o.f); o.tick(o); // put non empty document o.spy(o, "value", {"ok": true, "id": "file"}, "Put non empty document"); o.jio.put({"_id": "file", "title": "myFile"}, o.f); o.tick(o); // put an attachment without attachment id // error 22 -> attachment id required o.spy(o, "status", 22, "Put attachment without id"); o.jio.putAttachment({ "_id": "file", "_data": "0123456789", "_mimetype": "text/plain" }, o.f); o.tick(o); // put an attachment o.spy(o, "value", {"ok": true, "id": "file", "attachment": "attmt"}, "Put attachment"); o.jio.putAttachment({ "_id": "file", "_attachment": "attmt", "_data": "0123456789", "_mimetype": "text/plain" }, o.f); o.tick(o); // get document o.spy(o, "value", {"_id": "file", "title": "get_title"}, "Get document"); o.jio.get({"_id": "file"}, o.f); o.tick(o); // get attachment o.spy(o, "value", "0123456789", "Get attachment"); o.jio.getAttachment({"_id": "file", "_attachment": "attmt"}, o.f); o.tick(o); // remove document o.spy(o, "value", {"ok": true, "id": "file"}, "Remove document"); o.jio.remove({"_id": "file"}, o.f); o.tick(o); // remove attachment o.spy(o, "value", {"ok": true, "id": "file", "attachment": "attmt"}, "Remove attachment"); o.jio.removeAttachment({"_id": "file", "_attachment": "attmt"}, o.f); o.tick(o); o.jio.stop(); }); module ( "Jio Job Managing" ); test ("Several Jobs at the same time", function () { var o = generateTools(this); o.jio = JIO.newJio({"type":"dummyallok"}); o.spy(o, "value", {"ok": true, "id": "file"}, "job1", "f"); o.spy(o, "value", {"ok": true, "id": "file2"}, "job2", "f2"); o.spy(o, "value", {"ok": true, "id": "file3"}, "job3", "f3"); o.jio.put({"_id": "file", "content": "content"}, o.f); o.jio.put({"_id": "file2", "content": "content2"}, o.f2); o.jio.put({"_id": "file3", "content": "content3"}, o.f3); o.tick(o, 1000, "f"); o.tick(o, "f2"); o.tick(o, "f3"); o.jio.stop(); }); test ("Similar Jobs at the same time (Update)", function () { var o = generateTools(this); o.jio = JIO.newJio({"type":"dummyallok"}); o.spy(o, "value", {"ok": true, "id": "file"}, "job1 ok", "f"); o.spy(o, "value", {"ok": true, "id": "file"}, "job2 ok", "f2"); o.spy(o, "value", {"ok": true, "id": "file"}, "job3 ok", "f3"); o.jio.put({"_id": "file", "content": "content"}, o.f); // 1 o.jio.put({"_id": "file", "content": "content"}, o.f2); // 2 o.jio.put({"_id": "file", "content": "content"}, o.f3); // 3 deepEqual(getLastJob(o.jio.getId()).id, 1, "Check job queue"); o.tick(o, 1000, "f"); o.tick(o, "f2"); o.tick(o, "f3"); o.spy(o, "value", {"ok": true, "id": "file"}, "job4 ok", "f"); o.spy(o, "value", {"ok": true, "id": "file"}, "job5 ok", "f2"); o.spy(o, "value", {"ok": true, "id": "file"}, "job6 ok", "f3"); o.jio.put({"_id": "file", "content": "content"}, o.f); // 4 o.jio.remove({"_id": "file", "content": "content"}, o.f2); // 5 o.jio.put({"_id": "file", "content": "content"}, o.f3); // 6 deepEqual(getLastJob(o.jio.getId()).id, 5, "Check job queue"); o.tick(o, 1000, "f"); o.tick(o, "f2"); o.tick(o, "f3"); o.jio.stop(); }); test ("One document aim jobs at the same time (Wait for job(s))" , function () { var o = generateTools(this); o.jio = JIO.newJio({"type":"dummyallok"}); o.spy(o, "value", {"ok": true, "id": "file"}, "job1", "f"); o.spy(o, "value", {"ok": true, "id": "file"}, "job2", "f2"); o.spy(o, "value", {"ok": true, "id": "file"}, "job3", "f3"); o.jio.put({"_id": "file", "content": "content"}, o.f); o.testLastJobWaitForJob(undefined, "job1 is not waiting for someone"); o.jio.remove({"_id": "file", "content": "content"}, o.f2); o.testLastJobWaitForJob([1], "job2 is waiting"); o.jio.put({"_id": "file"}, o.f3); o.testLastJobWaitForJob([1, 2], "job3 is waiting"); o.tick(o, 1000, "f"); o.tick(o, "f2"); o.tick(o, "f3"); o.spy(o, "value", {"ok": true, "id": "lol"}, "Chaining callbacks"); o.jio.removeAttachment({ "_id": "lol", "_attachment": "hu" }, function (err, response) { if (err) { return; } o.jio.remove({"_id": "lol"}, o.f); }); o.tick(o, 10000); o.jio.stop(); }); test ("Server will be available soon (Wait for time)" , function () { var o = generateTools(this); o.max_retry = 3; o.jio = JIO.newJio({"type":"dummyall3tries"}); o.spy(o, "value", {"ok": true, "id": "file"}, "job1", "f"); o.jio.put({"_id": "file", "content": "content"}, {"max_retry": o.max_retry}, o.f); for (o.i = 0; o.i < o.max_retry - 1; o.i += 1) { o.waitUntilLastJobIs("on going"); o.waitUntilLastJobIs("wait"); o.testLastJobWaitForTime("job1 is waiting for time"); } o.tick(o, 1000, "f"); o.jio.stop(); }); module ( "Jio Restore"); test ("Restore old Jio", function() { var o = generateTools(this); o.jio = JIO.newJio({ "type": "dummyall3tries", "application_name": "jiotests" }); o.jio_id = o.jio.getId(); o.jio.put({"_id": "file", "title": "myFile"}, {"max_retry":3}, o.f); o.waitUntilLastJobIs("initial"); // "on going" or "wait" should work // xxx also test with o.waitUntilLastJobIs("on going") ? o.jio.close(); o.jio = JIO.newJio({ "type": "dummyallok", "application_name": "jiotests" }); o.waitUntilAJobExists(30000); // timeout 30 sec o.testLastJobLabel("put", "Job restored"); o.clock.tick(2000); ok(getLastJob(o.jio.getId()) === undefined, "Job executed"); o.clock.tick(1000); o.jio.stop(); }); module ( "Jio LocalStorage" ); test ("Post", function(){ var o = generateTools(this); o.jio = JIO.newJio({ "type": "local", "username": "upost", "application_name": "apost" }); // post without id o.spy (o, "jobstatus", "done", "Post without id"); o.jio.post({}, function (err, response) { var uuid; o.f(err, response); uuid = (err || response).id; ok(isUuid(uuid), "Uuid should look like " + "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx : " + uuid); }); o.tick(o); // post non empty document o.spy (o, "value", {"ok": true, "id": "post1"}, "Post"); o.jio.post({"_id": "post1", "title": "myPost1"}, o.f); o.tick(o); deepEqual( localstorage.getItem("jio/localstorage/upost/apost/post1"), { "_id": "post1", "title": "myPost1" }, "Check document" ); // post but document already exists o.spy (o, "status", 409, "Post but document already exists"); o.jio.post({"_id": "post1", "title": "myPost2"}, o.f); o.tick(o); o.jio.stop(); }); test ("Put", function(){ var o = generateTools(this); o.jio = JIO.newJio({ "type": "local", "username": "uput", "application_name": "aput" }); // put without id // error 20 -> document id required o.spy (o, "status", 20, "Put without id"); o.jio.put({}, o.f); o.tick(o); // put non empty document o.spy (o, "value", {"ok": true, "id": "put1"}, "Creates a document"); o.jio.put({"_id": "put1", "title": "myPut1"}, o.f); o.tick(o); // check document deepEqual( localstorage.getItem("jio/localstorage/uput/aput/put1"), { "_id": "put1", "title": "myPut1" }, "Check document" ); // put but document already exists o.spy (o, "value", {"ok": true, "id": "put1"}, "Update the document"); o.jio.put({"_id": "put1", "title": "myPut2"}, o.f); o.tick(o); // check document deepEqual( localstorage.getItem("jio/localstorage/uput/aput/put1"), { "_id": "put1", "title": "myPut2" }, "Check document" ); o.jio.stop(); }); test ("PutAttachment", function(){ var o = generateTools(this); o.jio = JIO.newJio({ "type": "local", "username": "uputattmt", "application_name": "aputattmt" }); // putAttachment without doc id // error 20 -> document id required o.spy(o, "status", 20, "PutAttachment without doc id"); o.jio.putAttachment({}, o.f); o.tick(o); // putAttachment without attachment id // error 22 -> attachment id required o.spy(o, "status", 22, "PutAttachment without attachment id"); o.jio.putAttachment({"_id": "putattmt1"}, o.f); o.tick(o); // putAttachment without document // error 404 -> not found o.spy(o, "status", 404, "PutAttachment without document"); o.jio.putAttachment({"_id": "putattmt1", "_attachment": "putattmt2"}, o.f); o.tick(o); // adding a document localstorage.setItem("jio/localstorage/uputattmt/aputattmt/putattmt1", { "_id": "putattmt1", "title": "myPutAttmt1" }); // putAttachment with document o.spy(o, "value", {"ok": true, "id": "putattmt1", "attachment": "putattmt2"}, "PutAttachment with document, without data"); o.jio.putAttachment({"_id": "putattmt1", "_attachment": "putattmt2"}, o.f); o.tick(o); // check document deepEqual( localstorage.getItem("jio/localstorage/uputattmt/aputattmt/putattmt1"), { "_id": "putattmt1", "title": "myPutAttmt1", "_attachments": { "putattmt2": { "length": 0, // md5("") "digest": "md5-d41d8cd98f00b204e9800998ecf8427e" } } }, "Check document" ); // check attachment deepEqual( localstorage.getItem( "jio/localstorage/uputattmt/aputattmt/putattmt1/putattmt2"), "", "Check attachment" ); // update attachment o.spy(o, "value", {"ok": true, "id": "putattmt1", "attachment": "putattmt2"}, "Update Attachment, with data"); o.jio.putAttachment({ "_id": "putattmt1", "_attachment": "putattmt2", "_data": "abc" }, o.f); o.tick(o); // check document deepEqual( localstorage.getItem("jio/localstorage/uputattmt/aputattmt/putattmt1"), { "_id": "putattmt1", "title": "myPutAttmt1", "_attachments": { "putattmt2": { "length": 3, // md5("abc") "digest": "md5-900150983cd24fb0d6963f7d28e17f72" } } }, "Check document" ); // check attachment deepEqual( localstorage.getItem( "jio/localstorage/uputattmt/aputattmt/putattmt1/putattmt2"), "abc", "Check attachment" ); o.jio.stop(); }); test ("Get", function(){ var o = generateTools(this); o.jio = JIO.newJio({ "type": "local", "username": "uget", "application_name": "aget" }); // get inexistent document o.spy(o, "status", 404, "Get inexistent document"); o.jio.get({"_id": "get1"}, o.f); o.tick(o); // get inexistent attachment o.spy(o, "status", 404, "Get inexistent attachment"); o.jio.getAttachment({"_id": "get1", "_attachment": "get2"}, o.f); o.tick(o); // adding a document o.doc_get1 = { "_id": "get1", "title": "myGet1" }; localstorage.setItem("jio/localstorage/uget/aget/get1", o.doc_get1); // get document o.spy(o, "value", o.doc_get1, "Get document"); o.jio.get({"_id": "get1"}, o.f); o.tick(o); // get inexistent attachment (document exists) o.spy(o, "status", 404, "Get inexistent attachment (document exists)"); o.jio.getAttachment({"_id": "get1", "_attachment": "get2"}, o.f); o.tick(o); // adding an attachment o.doc_get1["_attachments"] = { "get2": { "length": 2, // md5("de") "digest": "md5-5f02f0889301fd7be1ac972c11bf3e7d" } }; localstorage.setItem("jio/localstorage/uget/aget/get1", o.doc_get1); localstorage.setItem("jio/localstorage/uget/aget/get1/get2", "de"); // get attachment o.spy(o, "value", "de", "Get attachment"); o.jio.getAttachment({"_id": "get1", "_attachment": "get2"}, o.f); o.tick(o); o.jio.stop(); }); test ("Remove", function(){ var o = generateTools(this); o.jio = JIO.newJio({ "type": "local", "username": "uremove", "application_name": "aremove" }); // remove inexistent document o.spy(o, "status", 404, "Remove inexistent document"); o.jio.remove({"_id": "remove1"}, o.f); o.tick(o); // remove inexistent attachment o.spy(o, "status", 404, "Remove inexistent attachment"); o.jio.removeAttachment({"_id": "remove1", "_attachment": "remove2"}, o.f); o.tick(o); // adding a document + attmt localstorage.setItem("jio/localstorage/uremove/aremove/remove1", { "_id": "remove1", "title": "myRemove1", "_attachments": { "remove2": { "length": 4, "digest": "md5-blahblah" } } }); localstorage.setItem( "jio/localstorage/uremove/aremove/remove1/remove2", "fghi"); // remove attachment o.spy(o, "value", {"ok": true, "id": "remove1", "attachment": "remove2"}, "Remove document"); o.jio.removeAttachment({"_id": "remove1", "_attachment": "remove2"}, o.f); o.tick(o); // remove document o.spy(o, "value", {"ok": true, "id": "remove1"}, "Remove document"); o.jio.remove({"_id": "remove1"}, o.f); o.tick(o); // check document ok(localstorage.getItem("jio/localstorage/uremove/aremove/remove1")===null, "Check document is removed"); // adding a document + attmt localstorage.setItem("jio/localstorage/uremove/aremove/remove1", { "_id": "remove1", "title": "myRemove1", "_attachments": { "remove2": { "length": 4, "digest": "md5-blahblah" } } }); localstorage.setItem( "jio/localstorage/uremove/aremove/remove1/remove2", "fghi"); // remove attachment o.spy(o, "value", {"ok": true, "id": "remove1"}, "Remove document and attachment"); o.jio.remove({"_id": "remove1"}, o.f); o.tick(o); ok(localstorage.getItem("jio/localstorage/uremove/aremove/remove1" )===null, "Check document is removed"); ok(localstorage.getItem("jio/localstorage/uremove/aremove/remove1/remove2" )===null, "Check attachment is removed"); o.jio.stop(); }); test ("AllDocs", function(){ var o = generateTools(this), i, m = 15; o.jio = JIO.newJio({ "type": "local", "username": "ualldocs", "application_name": "aalldocs" }); o.localpath = "jio/localstorage/ualldocs/aalldocs"; // sample data o.titles = ["Shawshank Redemption", "Godfather", "Godfather 2", "Pulp Fiction", "The Good, The Bad and The Ugly", "12 Angry Men", "The Dark Knight", "Schindlers List", "Lord of the Rings - Return of the King", "Fight Club", "Star Wars Episode V", "Lord Of the Rings - Fellowship of the Ring", "One flew over the Cuckoo's Nest", "Inception", "Godfellas" ]; o.years = [1994,1972,1974,1994,1966,1957,2008,1993,2003,1999,1980,2001, 1975,2010,1990 ]; o.director = ["Frank Darabont", "Francis Ford Coppola", "Francis Ford Coppola", "Quentin Tarantino", "Sergio Leone", "Sidney Lumet", "Christopher Nolan", "Steven Spielberg", "Peter Jackson", "David Fincher", "Irvin Kershner", "Peter Jackson", "Milos Forman", "Christopher Nolan", " Martin Scorsese" ] for (i = 0; i < m; i += 1) { o.fakeDoc = {}; o.fakeDoc._id = "doc_"+(i < 10 ? "0"+i : i); o.fakeDoc.title = o.titles[i]; o.fakeDoc.year = o.years[i]; o.fakeDoc.author = o.director[i]; if (i === 5) { o.fakeDoc._attachments = { "att": { "digest": "md5-dontcare", "content_type": "text/plain", "length": 3 } }; localstorage.setItem(o.localpath + "/doc_05/att", "abc"); } localstorage.setItem(o.localpath+"/doc_"+(i < 10 ? "0"+i : i), o.fakeDoc); } // response o.allDocsResponse = {}; o.allDocsResponse.rows = []; o.allDocsResponse.total_rows = 15; for (i = 0; i < m; i += 1) { o.allDocsResponse.rows.push({ "id": "doc_"+(i < 10 ? "0"+i : i), "key": "doc_"+(i < 10 ? "0"+i : i), "value": {} }); }; // alldocs o.spy(o, "value", o.allDocsResponse, "All docs"); o.jio.allDocs(function (err, response) { if (response && response.rows) { response.rows.sort(function (a, b) { return a.id > b.id ? 1 : a.id < b.id ? -1 : 0; }); } o.f(err, response); }); o.tick(o); // include docs o.allDocsResponse = {}; o.allDocsResponse.rows = []; o.allDocsResponse.total_rows = m; for (i = 0; i < m; i += 1) { o.allDocsResponse.rows.push({ "id": "doc_"+(i < 10 ? "0"+i : i), "key": "doc_"+(i < 10 ? "0"+i : i), "value": {}, "doc": localstorage.getItem(o.localpath+"/doc_"+(i < 10 ? "0"+i : i)) }); }; // alldocs o.spy(o, "value", o.allDocsResponse, "All docs (include docs)"); o.jio.allDocs({"include_docs": true}, function (err, response) { if (response && response.rows) { response.rows.sort(function (a, b) { return a.id > b.id ? 1 : a.id < b.id ? -1 : 0; }); } o.f(err, response); }); o.tick(o); // complex queries o.thisShouldBeTheAnswer4 = {"total_rows": 0, "rows": []}; o.allDocsResponse.rows.forEach(function (row) { var new_row; if (row.doc.year >= 1980) { new_row = JSON.parse(JSON.stringify(row)); new_row.value.title = row.doc.title; new_row.value.year = row.doc.year; delete new_row.doc; o.thisShouldBeTheAnswer4.rows.push(new_row); o.thisShouldBeTheAnswer4.total_rows += 1; } }); o.thisShouldBeTheAnswer4.rows.sort(function (a, b) { return a.value.year > b.value.year ? -1 : a.value.year < b.value.year ? 1 : 0; }); o.thisShouldBeTheAnswer4.total_rows = 5; o.thisShouldBeTheAnswer4.rows.length = 5; o.spy(o, "value", o.thisShouldBeTheAnswer4, "allDocs (complex queries year >= 1980, all query options)"); o.jio.allDocs({ "query": '(year: >= "1980")', "limit": [0,5], "sort_on": [["year", "descending"]], "select_list": ["title", "year"] }, o.f); o.tick(o); // empty query returns all o.thisShouldBeTheAnswer5 = {"total_rows": 0, "rows": []}; o.allDocsResponse.rows.forEach(function (row) { var new_row = JSON.parse(JSON.stringify(row)); new_row.value.title = row.doc.title; o.thisShouldBeTheAnswer5.rows.push(new_row); o.thisShouldBeTheAnswer5.total_rows += 1; }); o.thisShouldBeTheAnswer5.rows.sort(function (a, b) { return a.value.title > b.value.title ? -1 : a.value.title < b.value.title ? 1 : 0; }); o.spy(o, "value", o.thisShouldBeTheAnswer5, "allDocs (empty query in complex query)"); o.jio.allDocs({ "sort_on": [["title", "descending"]], "select_list": ["title"], "include_docs": true }, o.f); o.tick(o); o.jio.stop(); }); module ( "Jio Revision Storage + Local Storage" ); test ("Post", function(){ var o = generateTools(this); o.jio = JIO.newJio({ "type": "revision", "sub_storage": { "type": "local", "username": "urevpost", "application_name": "arevpost" } }); o.localpath = "jio/localstorage/urevpost/arevpost"; // post without id o.revisions = {"start": 0, "ids": []}; o.spy (o, "status", undefined, "Post without id"); o.jio.post({}, function (err, response) { o.f.apply(arguments); o.uuid = (err || response).id; ok(isUuid(o.uuid), "Uuid should look like " + "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx : " + o.uuid); }); o.tick(o); o.rev = "1-"+generateRevisionHash({"_id": o.uuid}, o.revisions); // check document deepEqual( localstorage.getItem(o.localpath + "/" + o.uuid + "." + o.rev), {"_id": o.uuid + "." + o.rev}, "Check document" ); // check document tree o.doc_tree = { "_id": o.uuid + ".revision_tree.json", "children": [{ "rev": o.rev, "status": "available", "children": [] }] }; deepEqual( localstorage.getItem( o.localpath + "/" + o.uuid + ".revision_tree.json" ), o.doc_tree, "Check document tree" ); // post non empty document o.doc = {"_id": "post1", "title": "myPost1"}; o.rev = "1-"+generateRevisionHash(o.doc, o.revisions); o.spy (o, "value", {"ok": true, "id": "post1", "rev": o.rev}, "Post"); o.jio.post(o.doc, o.f); o.tick(o); // check document o.doc["_id"] = "post1."+o.rev; deepEqual( localstorage.getItem(o.localpath + "/post1." + o.rev), o.doc, "Check document" ); // check document tree o.doc_tree._id = "post1.revision_tree.json"; o.doc_tree.children[0] = { "rev": o.rev, "status": "available", "children": [] }; deepEqual( localstorage.getItem( o.localpath + "/post1.revision_tree.json" ), o.doc_tree, "Check document tree" ); // post and document already exists o.doc = {"_id": "post1", "title": "myPost2"}; o.rev = "1-"+generateRevisionHash(o.doc, o.revisions); o.spy (o, "value", { "ok": true, "id": "post1", "rev": o.rev }, "Post and document already exists"); o.jio.post(o.doc, o.f); o.tick(o); // check document o.doc["_id"] = "post1."+o.rev; deepEqual( localstorage.getItem(o.localpath + "/post1." + o.rev), o.doc, "Check document" ); // check document tree o.doc_tree._id = "post1.revision_tree.json"; o.doc_tree.children.unshift({ "rev": o.rev, "status": "available", "children": [] }); deepEqual( localstorage.getItem( o.localpath + "/post1.revision_tree.json" ), o.doc_tree, "Check document tree" ); // post + revision o.doc = {"_id": "post1", "_rev": o.rev, "title": "myPost2"}; o.revisions = {"start": 1, "ids": [o.rev.split('-')[1]]}; o.rev = "2-"+generateRevisionHash(o.doc, o.revisions); o.spy (o, "value", {"ok": true, "id": "post1", "rev": o.rev}, "Post + revision"); o.jio.post(o.doc, o.f); o.tick(o); // // keep_revision_history // ok (false, "keep_revision_history Option Not Implemented"); // check document o.doc["_id"] = "post1."+o.rev; delete o.doc._rev; deepEqual( localstorage.getItem(o.localpath + "/post1." + o.rev), o.doc, "Check document" ); // check document tree o.doc_tree._id = "post1.revision_tree.json"; o.doc_tree.children[0].children.unshift({ "rev": o.rev, "status": "available", "children": [] }); deepEqual( localstorage.getItem( o.localpath + "/post1.revision_tree.json" ), o.doc_tree, "Check document tree" ); // add attachment o.doc._attachments = { "attachment_test": { "length": 35, "digest": "A", "content_type": "oh/yeah" } }; localstorage.setItem(o.localpath + "/post1." + o.rev, o.doc); localstorage.setItem(o.localpath + "/post1." + o.rev + "/attachment_test", "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"); // post + attachment copy o.doc = {"_id": "post1", "_rev": o.rev, "title": "myPost2"}; o.revisions = { "start": 2, "ids": [o.rev.split('-')[1], o.revisions.ids[0]] }; o.rev = "3-"+generateRevisionHash(o.doc, o.revisions); o.spy (o, "value", {"ok": true, "id": "post1", "rev": o.rev}, "Post + attachment copy"); o.jio.post(o.doc, o.f); o.tick(o); // check attachment deepEqual( localstorage.getItem(o.localpath + "/post1." + o.rev + "/attachment_test"), "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "Check Attachment" ); // check document tree o.doc_tree._id = "post1.revision_tree.json"; o.doc_tree.children[0].children[0].children.unshift({ "rev": o.rev, "status": "available", "children": [] }); deepEqual( localstorage.getItem( o.localpath + "/post1.revision_tree.json" ), o.doc_tree, "Check document tree" ); // post + wrong revision o.doc = {"_id": "post1", "_rev": "3-wr3", "title": "myPost3"}; o.revisions = {"start": 3, "ids": ["wr3"]}; o.rev = "4-"+generateRevisionHash(o.doc, o.revisions); o.spy(o, "value", {"id": "post1", "ok": true, "rev": o.rev}, "Postt + wrong revision"); o.jio.post(o.doc, o.f); o.tick(o); // check document deepEqual( localstorage.getItem(o.localpath + "/post1.3-wr3"), null, "Check document" ); // check document o.doc._id = "post1." + o.rev; delete o.doc._rev; deepEqual( localstorage.getItem(o.localpath + "/post1." + o.rev), o.doc, "Check document" ); // check document tree o.doc_tree._id = "post1.revision_tree.json"; o.doc_tree.children.unshift({ "rev": "3-wr3", "status": "missing", "children": [{ "rev": o.rev, "status": "available", "children": [] }] }); deepEqual( localstorage.getItem( o.localpath + "/post1.revision_tree.json" ), o.doc_tree, "Check document tree" ); o.jio.stop(); }); test ("Put", function(){ var o = generateTools(this); o.jio = JIO.newJio({ "type": "revision", "sub_storage": { "type": "local", "username": "urevput", "application_name": "arevput" } }); o.localpath = "jio/localstorage/urevput/arevput"; // put without id // error 20 -> document id required o.spy (o, "status", 20, "Put without id"); o.jio.put({}, o.f); o.tick(o); // put non empty document o.doc = {"_id": "put1", "title": "myPut1"}; o.revisions = {"start": 0, "ids": []}; o.rev = "1-"+generateRevisionHash(o.doc, o.revisions); o.spy (o, "value", {"ok": true, "id": "put1", "rev": o.rev}, "Creates a document"); o.jio.put(o.doc, o.f); o.tick(o); // check document o.doc._id = "put1." + o.rev; deepEqual( localstorage.getItem(o.localpath + "/put1." + o.rev), o.doc, "Check document" ); // check document tree o.doc_tree = { "_id": "put1.revision_tree.json", "children": [{ "rev": o.rev, "status": "available", "children": [] }] }; deepEqual( localstorage.getItem( o.localpath + "/put1.revision_tree.json" ), o.doc_tree, "Check document tree" ); // put without rev and document already exists o.doc = {"_id": "put1", "title": "myPut2"}; o.rev = "1-"+generateRevisionHash(o.doc, o.revisions); o.spy (o, "value", {"ok": true, "id": "put1", "rev": o.rev}, "Put same document without revision"); o.jio.put(o.doc, o.f); o.tick(o); o.doc_tree.children.unshift({ "rev": o.rev, "status": "available", "children": [] }); // put + revision o.doc = {"_id": "put1", "_rev": o.rev, "title": "myPut2"}; o.revisions = {"start": 1, "ids": [o.rev.split('-')[1]]}; o.rev = "2-"+generateRevisionHash(o.doc, o.revisions); o.spy (o, "value", {"id": "put1", "ok": true, "rev": o.rev}, "Put + revision"); o.jio.put(o.doc, o.f); o.tick(o); // check document o.doc._id = "put1." + o.rev; delete o.doc._rev; deepEqual( localstorage.getItem(o.localpath + "/put1." + o.rev), o.doc, "Check document" ); // check document tree o.doc_tree.children[0].children.unshift({ "rev": o.rev, "status": "available", "children": [] }); deepEqual( localstorage.getItem( o.localpath + "/put1.revision_tree.json" ), o.doc_tree, "Check document tree" ); // put + wrong revision o.doc = {"_id": "put1", "_rev": "3-wr3", "title": "myPut3"}; o.revisions = {"start": 3, "ids": ["wr3"]}; o.rev = "4-"+generateRevisionHash(o.doc, o.revisions); o.spy (o, "value", {"id": "put1", "ok": true, "rev": o.rev}, "Put + wrong revision"); o.jio.put(o.doc, o.f); o.tick(o); // check document o.doc._id = "put1." + o.rev; delete o.doc._rev; deepEqual( localstorage.getItem(o.localpath + "/put1." + o.rev), o.doc, "Check document" ); // check document tree o.doc_tree.children.unshift({ "rev": "3-wr3", "status": "missing", "children": [{ "rev": o.rev, "status": "available", "children": [] }] }); deepEqual( localstorage.getItem( o.localpath + "/put1.revision_tree.json" ), o.doc_tree, "Check document tree" ); // put + revision history o.doc = { "_id": "put1", //"_revs": ["3-rh3", "2-rh2", "1-rh1"], // same as below "_revs": {"start": 3, "ids": ["rh3", "rh2", "rh1"]}, "title": "myPut3" }; o.spy (o, "value", {"id": "put1", "ok": true, "rev": "3-rh3"}, "Put + revision history"); o.jio.put(o.doc, o.f); o.tick(o); // check document o.doc._id = "put1.3-rh3"; delete o.doc._revs; deepEqual( localstorage.getItem(o.localpath + "/put1.3-rh3"), o.doc, "Check document" ); // check document tree o.doc_tree.children.unshift({ "rev": "1-rh1", "status": "missing", "children": [{ "rev": "2-rh2", "status": "missing", "children": [{ "rev": "3-rh3", "status": "available", "children": [] }] }] }); deepEqual( localstorage.getItem( o.localpath + "/put1.revision_tree.json" ), o.doc_tree, "Check document tree" ); // add attachment o.doc._attachments = { "att1": { "length": 1, "content_type": "text/plain", "digest": "md5-0cc175b9c0f1b6a831c399e269772661" }, "att2": { "length": 2, "content_type": "dont/care", "digest": "md5-5360af35bde9ebd8f01f492dc059593c" } }; localstorage.setItem(o.localpath + "/put1.3-rh3", o.doc); localstorage.setItem(o.localpath + "/put1.3-rh3/att1", "a"); localstorage.setItem(o.localpath + "/put1.3-rh3/att2", "bc"); // put + revision with attachment o.attachments = o.doc._attachments; o.doc = {"_id": "put1", "_rev": "3-rh3", "title": "myPut4"}; o.revisions = {"start": 3, "ids": ["rh3","rh2","rh1"]}; o.rev = "4-"+generateRevisionHash(o.doc, o.revisions); o.spy (o, "value", {"id": "put1", "ok": true, "rev": o.rev}, "Put + revision (document contains attachments)"); o.jio.put(o.doc, o.f); o.tick(o); // check document o.doc._id = "put1." + o.rev; o.doc._attachments = o.attachments; delete o.doc._rev; deepEqual( localstorage.getItem(o.localpath + "/put1." + o.rev), o.doc, "Check document" ); // check attachments deepEqual( localstorage.getItem(o.localpath + "/put1." + o.rev + "/att1"), "a", "Check Attachment" ); deepEqual( localstorage.getItem(o.localpath + "/put1." + o.rev + "/att2"), "bc", "Check Attachment" ); // check document tree o.doc_tree.children[0].children[0].children[0].children.unshift({ "rev": o.rev, "status": "available", "children": [] }); deepEqual( localstorage.getItem( o.localpath + "/put1.revision_tree.json" ), o.doc_tree, "Check document tree" ); o.jio.stop(); }); test("Put Attachment", function () { var o = generateTools(this); o.jio = JIO.newJio({ "type": "revision", "sub_storage": { "type": "local", "username": "urevputattmt", "application_name": "arevputattmt" } }); // putAttachment without doc id // error 20 -> document id required o.spy(o, "status", 20, "PutAttachment without doc id" + " -> 20 document id required"); o.jio.putAttachment({}, o.f); o.tick(o); // putAttachment without attachment id // erorr 22 -> attachment id required o.spy(o, "status", 22, "PutAttachment without attachment id" + " -> 22 attachment id required"); o.jio.putAttachment({"_id": "putattmt1"}, o.f); o.tick(o); // putAttachment without document o.revisions = {"start": 0, "ids": []} o.rev_hash = generateRevisionHash({"_id": "doc1", "_attachment": "attmt1"}, o.revisions); o.rev = "1-" + o.rev_hash; o.spy(o, "value", {"ok": true, "id": "doc1", "attachment": "attmt1", "rev": o.rev}, "PutAttachment without document, without data"); o.jio.putAttachment({"_id": "doc1", "_attachment": "attmt1"}, o.f); o.tick(o); // check document deepEqual( localstorage.getItem( "jio/localstorage/urevputattmt/arevputattmt/doc1." + o.rev ), { "_id": "doc1." + o.rev, "_attachments": { "attmt1": { "length": 0, // md5("") "digest": "md5-d41d8cd98f00b204e9800998ecf8427e" } } }, "Check document" ); // check attachment deepEqual( localstorage.getItem( "jio/localstorage/urevputattmt/arevputattmt/doc1." + o.rev + "/attmt1" ), "", "Check attachment" ); // adding a metadata to the document o.doc = localstorage.getItem( "jio/localstorage/urevputattmt/arevputattmt/doc1." + o.rev ); o.doc.title = "My Title"; localstorage.setItem( "jio/localstorage/urevputattmt/arevputattmt/doc1." + o.rev, o.doc ); // update attachment o.prev_rev = o.rev; o.revisions = {"start": 1, "ids": [o.rev_hash]} o.rev_hash = generateRevisionHash({ "_id": "doc1", "_data": "abc", "_attachment": "attmt1", }, o.revisions); o.rev = "2-" + o.rev_hash; o.spy(o, "value", {"ok": true, "id": "doc1", "attachment": "attmt1", "rev": o.rev}, "Update Attachment, with data"); o.jio.putAttachment({ "_id": "doc1", "_data": "abc", "_attachment": "attmt1", "_rev": o.prev_rev }, o.f); o.tick(o); // check document deepEqual( localstorage.getItem( "jio/localstorage/urevputattmt/arevputattmt/doc1." + o.rev ), { "_id": "doc1." + o.rev, "title": "My Title", "_attachments": { "attmt1": { "length": 3, // md5("abc") "digest": "md5-900150983cd24fb0d6963f7d28e17f72" } } }, "Check document" ); // check attachment deepEqual( localstorage.getItem( "jio/localstorage/urevputattmt/arevputattmt/doc1." + o.rev + "/attmt1" ), "abc", "Check attachment" ); // putAttachment new attachment o.prev_rev = o.rev; o.revisions = {"start": 2, "ids": [o.rev_hash, o.revisions.ids[0]]} o.rev_hash = generateRevisionHash({ "_id": "doc1", "_data": "def", "_attachment": "attmt2", }, o.revisions); o.rev = "3-" + o.rev_hash; o.spy(o, "value", {"ok": true, "id": "doc1", "attachment": "attmt2", "rev": o.rev}, "PutAttachment without document, without data"); o.jio.putAttachment({ "_id": "doc1", "_data": "def", "_attachment": "attmt2", "_rev": o.prev_rev }, o.f); o.tick(o); // check document deepEqual( localstorage.getItem( "jio/localstorage/urevputattmt/arevputattmt/doc1." + o.rev ), { "_id": "doc1." + o.rev, "title": "My Title", "_attachments": { "attmt1": { "length": 3, "digest": "md5-900150983cd24fb0d6963f7d28e17f72" }, "attmt2": { "length": 3, // md5("def") "digest": "md5-4ed9407630eb1000c0f6b63842defa7d" } } }, "Check document" ); // check attachment deepEqual( localstorage.getItem( "jio/localstorage/urevputattmt/arevputattmt/doc1." + o.rev + "/attmt2" ), "def", "Check attachment" ); o.jio.stop(); }); test ("Get", function(){ var o = generateTools(this); o.jio = JIO.newJio({ "type": "revision", "sub_storage": { "type": "local", "username": "urevget", "application_name": "arevget" } }); o.localpath = "jio/localstorage/urevget/arevget"; // get inexistent document o.spy(o, "status", 404, "Get inexistent document (winner)" + " -> 404 Not Found"); o.jio.get({"_id": "get1"}, o.f); o.tick(o); // get inexistent attachment o.spy(o, "status", 404, "Get inexistent attachment (winner)" + " -> 404 Not Found"); o.jio.getAttachment({"_id": "get1", "_attachment": "get2"}, o.f); o.tick(o); // adding a document o.doctree = {"children":[{ "rev": "1-rev1", "status": "available", "children": [] }]}; o.doc_myget1 = {"_id": "get1.1-rev1", "title": "myGet1"}; localstorage.setItem(o.localpath+"/get1.revision_tree.json", o.doctree); localstorage.setItem(o.localpath+"/get1.1-rev1", o.doc_myget1); // get document o.doc_myget1_cloned = clone(o.doc_myget1); o.doc_myget1_cloned._id = "get1"; o.doc_myget1_cloned._rev = "1-rev1"; o.doc_myget1_cloned._revisions = {"start": 1, "ids": ["rev1"]}; o.doc_myget1_cloned._revs_info = [{ "rev": "1-rev1", "status": "available" }]; o.spy(o, "value", o.doc_myget1_cloned, "Get document (winner)"); o.jio.get({"_id": "get1"}, {"revs_info": true, "revs": true, "conflicts": true}, o.f); o.tick(o); // adding two documents o.doctree = {"children":[{ "rev": "1-rev1", "status": "available", "children": [] },{ "rev": "1-rev2", "status": "available", "children": [{ "rev": "2-rev3", "status": "available", "children": [] }] }]}; o.doc_myget2 = {"_id": "get1.1-rev2", "title": "myGet2"}; o.doc_myget3 = {"_id": "get1.2-rev3", "title": "myGet3"}; localstorage.setItem(o.localpath+"/get1.revision_tree.json", o.doctree); localstorage.setItem(o.localpath+"/get1.1-rev2", o.doc_myget2); localstorage.setItem(o.localpath+"/get1.2-rev3", o.doc_myget3); // get document o.doc_myget3_cloned = clone(o.doc_myget3); o.doc_myget3_cloned._id = "get1"; o.doc_myget3_cloned["_rev"] = "2-rev3"; o.doc_myget3_cloned["_revisions"] = {"start": 2, "ids": ["rev3","rev2"]}; o.doc_myget3_cloned["_revs_info"] = [{ "rev": "2-rev3", "status": "available" },{ "rev": "1-rev2", "status": "available" }]; o.doc_myget3_cloned["_conflicts"] = ["1-rev1"]; o.spy(o, "value", o.doc_myget3_cloned, "Get document (winner, after posting another one)"); o.jio.get({"_id": "get1"}, {"revs_info": true, "revs": true, "conflicts": true}, o.f); o.tick(o); // get inexistent specific document o.spy(o, "status", 404, "Get document (inexistent specific revision)" + " -> 404 Not Found"); o.jio.get({"_id": "get1", "_rev": "1-rev0"}, { "revs_info": true, "revs": true, "conflicts": true, }, o.f); o.tick(o); // get specific document o.doc_myget2_cloned = clone(o.doc_myget2); o.doc_myget2_cloned._id = "get1"; o.doc_myget2_cloned["_rev"] = "1-rev2"; o.doc_myget2_cloned["_revisions"] = {"start": 1, "ids": ["rev2"]}; o.doc_myget2_cloned["_revs_info"] = [{ "rev": "1-rev2", "status": "available" }]; o.doc_myget2_cloned["_conflicts"] = ["1-rev1"]; o.spy(o, "value", o.doc_myget2_cloned, "Get document (specific revision)"); o.jio.get({"_id": "get1", "_rev": "1-rev2"}, { "revs_info": true, "revs": true, "conflicts": true, }, o.f); o.tick(o); // adding an attachment o.attmt_myget3 = { "get2": { "length": 3, "digest": "md5-dontcare", "content_type": "oh/yeah" } }; o.doc_myget3._attachments = o.attmt_myget3; localstorage.setItem(o.localpath+"/get1.2-rev3", o.doc_myget3); localstorage.setItem(o.localpath+"/get1.2-rev3/get2", "abc"); // get attachment winner o.spy(o, "value", "abc", "Get attachment (winner)"); o.jio.getAttachment({"_id": "get1", "_attachment": "get2"}, o.f); o.tick(o); // get inexistent attachment specific rev o.spy(o, "status", 404, "Get inexistent attachment (specific revision)" + " -> 404 Not Found"); o.jio.getAttachment({ "_id": "get1", "_attachment": "get2", "_rev": "1-rev1" }, { "revs_info": true, "revs": true, "conflicts": true, }, o.f); o.tick(o); // get attachment specific rev o.spy(o, "value", "abc", "Get attachment (specific revision)"); o.jio.getAttachment({ "_id": "get1", "_attachment": "get2", "_rev": "2-rev3" }, { "revs_info": true, "revs": true, "conflicts": true, }, o.f); o.tick(o); // get document with attachment (specific revision) delete o.doc_myget2_cloned._attachments; o.spy(o, "value", o.doc_myget2_cloned, "Get document which have an attachment (specific revision)"); o.jio.get({"_id": "get1", "_rev": "1-rev2"}, { "revs_info": true, "revs": true, "conflicts": true, }, o.f); o.tick(o); // get document with attachment (winner) o.doc_myget3_cloned._attachments = o.attmt_myget3; o.spy(o, "value", o.doc_myget3_cloned, "Get document which have an attachment (winner)"); o.jio.get({"_id": "get1"}, {"revs_info": true, "revs": true, "conflicts": true}, o.f); o.tick(o); o.jio.stop(); }); test ("Remove", function(){ var o = generateTools(this); o.jio = JIO.newJio({ "type": "revision", "sub_storage": { "type": "local", "username": "urevrem", "application_name": "arevrem" } }); o.localpath = "jio/localstorage/urevrem/arevrem"; // 1. remove document without revision o.spy(o, "status", 409, "Remove document without revision " + "-> 409 Conflict"); o.jio.remove({"_id":"remove1"}, o.f); o.tick(o); // 2. remove attachment without revision o.spy(o, "status", 409, "Remove attachment without revision " + "-> 409 Conflict"); o.jio.removeAttachment({"_id":"remove1", "_attachment": "remove2"}, o.f); o.tick(o); // adding a document with attachments o.doc_myremove1 = { "_id": "remove1.1-veryoldrev", "title": "myRemove1" }; localstorage.setItem(o.localpath + "/remove1.1-veryoldrev", o.doc_myremove1); o.doc_myremove1._id = "remove1.2-oldrev"; o.attachment_remove2 = { "length": 3, "digest": "md5-dontcare", "content_type": "oh/yeah" } o.attachment_remove3 = { "length": 5, "digest": "md5-865f5cc7fbd7854902eae9d8211f178a", "content_type": "he/ho" } o.doc_myremove1._attachments = { "remove2": o.attachment_remove2, "remove3": o.attachment_remove3 }; localstorage.setItem(o.localpath + "/remove1.2-oldrev", o.doc_myremove1); localstorage.setItem(o.localpath + "/remove1.2-oldrev/remove2", "abc"); localstorage.setItem(o.localpath + "/remove1.2-oldrev/remove3", "defgh"); // add document tree o.doctree = { "children": [{ "rev": "1-veryoldrev", "status": "available", "children": [{ "rev": "2-oldrev", "status": "available", "children": [] }] }] }; localstorage.setItem(o.localpath + "/remove1.revision_tree.json", o.doctree); // 3. remove inexistent attachment o.spy(o, "status", 404, "Remove inexistent attachment -> 404 Not Found"); o.jio.removeAttachment({ "_id": "remove1", "_attachment": "remove0", "_rev": "2-oldrev" }, o.f); o.tick(o); // 4. remove existing attachment o.rev_hash = generateRevisionHash({ "_id": "remove1", "_attachment": "remove2", }, {"start": 2, "ids": ["oldrev", "veryoldrev"]}); o.spy (o, "value", { "ok": true, "id": "remove1", "attachment": "remove2", "rev": "3-" + o.rev_hash }, "Remove existing attachment"); o.jio.removeAttachment({ "_id": "remove1", "_attachment": "remove2", "_rev": "2-oldrev" }, o.f); o.tick(o); o.doctree = { "_id": "remove1.revision_tree.json", "children":[{ "rev": "1-veryoldrev", "status": "available", "children": [{ "rev": "2-oldrev", "status": "available", "children": [{ "rev": "3-" + o.rev_hash, "status": "available", "children": [] }] }] }] }; // 5. check if document tree has been updated correctly deepEqual(localstorage.getItem( o.localpath + "/remove1.revision_tree.json" ), o.doctree, "Check document tree"); // 6. check if the attachment still exists deepEqual(localstorage.getItem( o.localpath + "/remove1.2-oldrev/remove2" ), "abc", "Check attachment -> still exists"); // 7. check if document is updated deepEqual(localstorage.getItem( o.localpath + "/remove1.3-" + o.rev_hash ), { "_id": "remove1.3-" + o.rev_hash, "title":"myRemove1", "_attachments": {"remove3": o.attachment_remove3} }, "Check document"); // 8. remove document with wrong revision o.spy(o, "status", 409, "Remove document with wrong revision " + "-> 409 Conflict"); o.jio.remove({"_id":"remove1", "_rev": "1-a"}, o.f); o.tick(o); // 9. remove attachment wrong revision o.spy(o, "status", 409, "Remove attachment with wrong revision " + "-> 409 Conflict"); o.jio.removeAttachment({ "_id": "remove1", "_attachment": "remove2", "_rev": "1-a" }, o.f); o.tick(o); // 10. remove document o.last_rev = "3-" + o.rev_hash; o.rev_hash = generateRevisionHash( {"_id": "remove1"}, {"start": 3, "ids": [o.rev_hash, "oldrev", "veryoldrev"]}, true ); o.spy(o, "value", {"ok": true, "id": "remove1", "rev": "4-" + o.rev_hash}, "Remove document"); o.jio.remove({"_id":"remove1", "_rev": o.last_rev}, o.f); o.tick(o); // 11. check document tree o.doctree.children[0].children[0].children[0].children.unshift({ "rev": "4-" + o.rev_hash, "status": "deleted", "children": [] }); deepEqual(localstorage.getItem(o.localpath + "/remove1.revision_tree.json"), o.doctree, "Check document tree"); o.jio.stop(); }); test("allDocs", function () { var o = generateTools(this); o.jio = JIO.newJio({ "type": "revision", "sub_storage": { "type": "local", "username": "urevad1", "application_name": "arevad1" } }); o.localpath = "jio/localstorage/urevad1/arevad1"; // adding 3 documents o.jio.put({"_id": "yes"}, function (err, response) { o.rev1 = (response || {}).rev; }); o.jio.put({"_id": "no"}, function (err, response) { o.rev2 = (response || {}).rev; }); o.jio.put({"_id": "maybe"}, function (err, response) { o.rev3 = (response || {}).rev; }); o.clock.tick(1000); // adding conflicts o.jio.put({"_id": "maybe"}); // adding 2 attachments o.jio.putAttachment({ "_id": "yes", "_attachment": "blue", "_mimetype": "text/plain", "_rev": o.rev1, "_data": "sky" }, function (err, response) { o.rev1 = (response || {}).rev; }); o.jio.putAttachment({ "_id": "no", "_attachment": "Heeeee!", "_mimetype": "text/plain", "_rev": o.rev2, "_data": "Hooooo!" }, function (err, response) { o.rev2 = (response || {}).rev; }); o.clock.tick(1000); o.rows = { "total_rows": 3, "rows": [{ "id": "maybe", "key": "maybe", "value": { "rev": o.rev3 } }, { "id": "no", "key": "no", "value": { "rev": o.rev2 } }, { "id": "yes", "key": "yes", "value": { "rev": o.rev1 } }] }; o.spy(o, "value", o.rows, "allDocs"); o.jio.allDocs(function (err, response) { if (response && response.rows) { response.rows.sort(function (a, b) { return a.id > b.id ? 1 : a.id < b.id ? -1 : 0; }) } o.f(err, response); }); o.tick(o); o.rows.rows[0].doc = { "_id": "maybe", "_rev": o.rev3 }; o.rows.rows[1].doc = { "_id": "no", "_rev": o.rev2, "_attachments": { "Heeeee!": { "content_type": "text/plain", "digest": "md5-2686969b0bc0fd9bc186146a1ecb09a7", "length": 7 } }, }; o.rows.rows[2].doc = { "_id": "yes", "_rev": o.rev1, "_attachments": { "blue": { "content_type": "text/plain", "digest": "md5-900bc885d7553375aec470198a9514f3", "length": 3 } }, }; o.spy(o, "value", o.rows, "allDocs + include docs"); o.jio.allDocs({"include_docs": true}, function (err, response) { if (response && response.rows) { response.rows.sort(function (a, b) { return a.id > b.id ? 1 : a.id < b.id ? -1 : 0; }) } o.f(err, response); }); o.tick(o); o.jio.stop(); }); test ("Scenario", function(){ var o = generateTools(this); o.jio = JIO.newJio({ "type": "revision", "sub_storage": { "type": "local", "username": "usam1", "application_name": "asam1" } }); o.localpath = "jio/localstorage/usam1/asam1"; // new application ok ( o.jio, "I open my application with revision and localstorage"); // put non empty document A-1 o.doc = {"_id": "sample1", "title": "mySample1"}; o.revisions = {"start": 0, "ids": []}; o.hex = generateRevisionHash(o.doc, o.revisions); o.rev = "1-"+o.hex; o.spy (o, "value", {"ok": true, "id": "sample1", "rev": o.rev}, "Then, I create a new document (no attachment), my application "+ "keep the revision in memory"); o.jio.put(o.doc, o.f); o.tick(o); // open new tab (JIO) o.jio2 = JIO.newJio({ "type": "revision", "sub_storage": { "type": "local", "username": "usam1", "application_name": "asam1" } }); o.localpath = "jio/localstorage/usam1/asam1"; // Create a new JIO in a new tab ok (o.jio2, "Now, I am opening a new tab, with the same application"+ " and the same storage tree"); // Get the document from the first storage o.doc._rev = o.rev; o.doc._revisions = {"ids":[o.hex], "start":1 }; o.doc._revs_info = [{"rev": o.rev, "status": "available"}]; o.spy(o, "value", o.doc, "And, on this new tab, I load the document,"+ "and my application keep the revision in memory"); o.jio2.get({"_id": "sample1", "_rev": o.rev}, { "revs_info": true, "revs": true, "conflicts": true, }, o.f); o.tick(o); // MODIFY the 2nd version o.doc_2 = {"_id": "sample1", "_rev": o.rev, "title":"mySample2_modified"}; o.revisions_2 = {"start":1 , "ids":[o.hex]}; o.hex_2 = generateRevisionHash(o.doc_2, o.revisions_2) o.rev_2 = "2-"+o.hex_2; o.spy (o, "value", {"id":"sample1", "ok":true, "rev": o.rev_2}, "So, I can modify and update it"); o.jio2.put(o.doc_2, o.f); o.tick(o); // MODIFY first version o.doc_1 = { "_id": "sample1", "_rev": o.rev, "title": "mySample1_modified" }; o.revisions_1 = {"start": 1, "ids":[o.rev.split('-')[1] ]}; o.hex_1 = generateRevisionHash(o.doc_1, o.revisions_1); o.rev_1 = "2-"+o.hex_1; o.spy (o, "value", {"id":"sample1", "ok":true, "rev": o.rev_1}, "Back to the first tab, I update the document."); o.jio.put(o.doc_1, o.f); o.tick(o); // Close 1st tab o.jio.close(); // Close 2nd tab o.jio2.close(); ok ( o.jio2, "I close tab both tabs"); // Reopen JIO o.jio = JIO.newJio({ "type": "revision", "sub_storage": { "type": "local", "username": "usam1", "application_name": "asam1" } }); o.localpath = "jio/localstorage/usam1/asam1"; ok ( o.jio, "Later, I open my application again"); // GET document without revision = winner & conflict! o.mydocSample3 = {"_id": "sample1", "title": "mySample1_modified", "_rev": o.rev_1}; o.mydocSample3._conflicts = [o.rev_2] o.mydocSample3._revs_info = [{"rev": o.rev_1, "status": "available"},{ "rev":o.rev,"status":"available" }]; o.mydocSample3._revisions = {"ids":[o.hex_1, o.hex], "start":2 }; o.spy(o, "value", o.mydocSample3, "I load the same document as before, and a popup shows that "+ "there is a conflict"); o.jio.get({"_id": "sample1"}, {"revs_info": true, "revs": true, "conflicts": true, }, o.f); o.tick(o); // REMOVE one of the two conflicting versions o.revisions = {"start": 2, "ids":[ o.rev_1.split('-')[1],o.rev.split('-')[1] ]}; o.doc_myremove3 = {"_id": "sample1", "_rev": o.rev_1}; o.rev_3 = "3-"+generateRevisionHash(o.doc_myremove3, o.revisions,true); o.spy (o, "value", {"ok": true, "id": "sample1", "rev": o.rev_3}, "I choose one of the document and close the application."); o.jio.remove({"_id":"sample1", "_rev":o.rev_1}, o.f); o.tick(o); // check to see if conflict still exists o.mydocSample4 = {"_id": "sample1", "title": "mySample2_modified", "_rev": o.rev_2}; o.mydocSample4._revs_info = [{"rev": o.rev_2, "status": "available"},{ "rev":o.rev,"status":"available" }]; o.mydocSample4._revisions = {"ids":[o.hex_2, o.hex], "start":2 }; o.spy(o, "value", o.mydocSample4, "Test if conflict still exists"); o.jio.get({"_id": "sample1"}, {"revs_info": true, "revs": true, "conflicts": true,}, o.f); o.tick(o); // END o.jio.stop(); }); module ("JIO Replicate Revision Storage"); var testReplicateRevisionStorage = function (sinon, jio_description) { var o = generateTools(sinon), leavesAction, generateLocalPath; o.jio = JIO.newJio(jio_description); generateLocalPath = function (storage_description) { return "jio/localstorage/" + storage_description.username + "/" + storage_description.application_name; }; leavesAction = function (action, storage_description, param) { var i; if (param === undefined) { param = {}; } else { param = clone(param); } if (storage_description.storage_list !== undefined) { // it is the replicate revision storage tree for (i = 0; i < storage_description.storage_list.length; i += 1) { leavesAction(action, storage_description.storage_list[i], param); } } else if (storage_description.sub_storage !== undefined) { // it is the revision storage tree param.revision = true; leavesAction(action, storage_description.sub_storage, param); } else { // it is the storage tree leaf param[storage_description.type] = true; action(storage_description, param); } }; o.leavesAction = function (action) { leavesAction(action, jio_description); }; // post a new document without id o.doc = {"title": "post document without id"}; o.spy(o, "status", undefined, "Post document (without id)"); o.jio.post(o.doc, function (err, response) { o.f.apply(arguments); o.response_rev = (err || response).rev; if (isUuid((err || response).id)) { ok(true, "Uuid format"); o.uuid = (err || response).id; } else { deepEqual((err || response).id, "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx", "Uuid format"); } }); o.tick(o); // check document o.doc._id = o.uuid; o.revisions = {"start": 0, "ids": []}; o.rev_hash = generateRevisionHash(o.doc, o.revisions); o.rev = "1-" + o.rev_hash; o.leavesAction(function (storage_description, param) { var suffix = "", doc = clone(o.doc); if (param.revision) { deepEqual(o.response_rev, o.rev, "Check revision"); doc._id += "." + o.rev; suffix = "." + o.rev; } deepEqual( localstorage.getItem(generateLocalPath(storage_description) + "/" + o.uuid + suffix), doc, "Check document" ); }); // get the post document without revision o.spy(o, "value", { "_id": o.uuid, "title": "post document without id", "_rev": o.rev, "_revisions": {"start": 1, "ids": [o.rev_hash]}, "_revs_info": [{"rev": o.rev, "status": "available"}] }, "Get the generated document, the winner"); o.jio.get({"_id": o.uuid}, { "conflicts": true, "revs": true, "revs_info": true }, o.f); o.tick(o); // post a new document with id o.doc = {"_id": "doc1", "title": "post new doc with id"}; o.rev1_1_hash = generateRevisionHash(o.doc, o.revisions); o.rev1_1 = "1-" + o.rev1_1_hash; o.rev1_1_history = {"start": 1, "ids": [o.rev1_1_hash]}; o.rev1_1_revs_info = [{"rev": o.rev1_1, "status": "available"}]; o.spy(o, "value", {"ok": true, "id": "doc1", "rev": o.rev1_1}, "Post new document with an id"); o.jio.post(o.doc, o.f); o.tick(o); // / // | // 1-1 // check document o.leavesAction(function (storage_description, param) { var suffix = "", doc = clone(o.doc); if (param.revision) { doc._id += "." + o.rev1_1; suffix = "." + o.rev1_1; } deepEqual( localstorage.getItem(generateLocalPath(storage_description) + "/doc1" + suffix), doc, "Check document" ); }); // get the post document without revision o.spy(o, "value", { "_id": "doc1", "title": "post new doc with id", "_rev": o.rev1_1, "_revisions": {"start": 1, "ids": [o.rev1_1_hash]}, "_revs_info": [{"rev": o.rev1_1, "status": "available"}] }, "Get the previous document (without revision)"); o.jio.get({"_id": "doc1"}, { "conflicts": true, "revs": true, "revs_info": true }, o.f); o.tick(o); // post same document without revision o.doc = {"_id": "doc1", "title": "post same document without revision"}; o.rev1_2_hash = generateRevisionHash(o.doc, o.revisions); o.rev1_2 = "1-" + o.rev1_2_hash; o.rev1_2_history = {"start": 1, "ids": [o.rev1_2_hash]}; o.rev1_2_revs_info = [{"rev": o.rev1_2, "status": "available"}]; o.spy(o, "value", {"ok": true, "id": "doc1", "rev": o.rev1_2}, "Post same document (without revision)"); o.jio.post(o.doc, o.f); o.tick(o); // / // / \ // 1-1 1-2 // check document o.leavesAction(function (storage_description, param) { var suffix = "", doc = clone(o.doc); if (param.revision) { doc._id += "." + o.rev1_2; suffix = "." + o.rev1_2; } deepEqual( localstorage.getItem(generateLocalPath(storage_description) + "/doc1" + suffix), doc, "Check document" ); }); // post a new revision o.doc = {"_id": "doc1", "title": "post new revision", "_rev": o.rev1_2}; o.revisions.start += 1; o.revisions.ids.unshift(o.rev1_2_hash); o.rev2_3_hash = generateRevisionHash(o.doc, o.revisions); o.rev2_3 = "2-" + o.rev2_3_hash; o.rev2_3_history = clone(o.rev1_2_history); o.rev2_3_history.start += 1; o.rev2_3_history.ids.unshift(o.rev2_3_hash); o.rev2_3_revs_info = clone(o.rev1_2_revs_info); o.rev2_3_revs_info.unshift({"rev": o.rev2_3, "status": "available"}); o.spy(o, "value", {"ok": true, "id": "doc1", "rev": o.rev2_3}, "Post document (with revision)"); o.jio.post(o.doc, o.f); o.tick(o); // / // / \ // 1-1 1-2 // | // 2-3 // check document o.leavesAction(function (storage_description, param) { var suffix = "", doc = clone(o.doc); delete doc._rev; if (param.revision) { doc._id += "." + o.rev2_3; suffix = "." + o.rev2_3; } deepEqual( localstorage.getItem(generateLocalPath(storage_description) + "/doc1" + suffix), doc, "Check document" ); }); // get the post document with revision o.spy(o, "value", { "_id": "doc1", "title": "post same document without revision", "_rev": o.rev1_2, "_revisions": {"start": 1, "ids": [o.rev1_2_hash]}, "_revs_info": [{"rev": o.rev1_2, "status": "available"}], "_conflicts": [o.rev1_1] }, "Get the previous document (with revision)"); o.jio.get({"_id": "doc1", "_rev": o.rev1_2}, { "conflicts": true, "revs": true, "revs_info": true }, o.f); o.tick(o); // put document without id o.spy(o, "status", 20, "Put document without id") o.jio.put({}, o.f); o.tick(o); // put document without rev o.doc = {"_id": "doc1", "title": "put new document"}; o.rev1_4_hash = generateRevisionHash(o.doc, {"start": 0, "ids": []}); o.rev1_4 = "1-" + o.rev1_4_hash; o.rev1_4_history = {"start": 1, "ids": [o.rev1_4_hash]}; o.rev1_4_revs_info = [{"rev": o.rev1_4, "status": "available"}]; o.spy(o, "value", {"id": "doc1", "ok": true, "rev": o.rev1_4}, "Put document without rev") o.jio.put(o.doc, o.f); o.tick(o); // __/__ // / | \ // 1-1 1-2 1-4 // | // 2-3 // put new revision o.doc = {"_id": "doc1", "title": "put new revision", "_rev": o.rev1_4}; o.rev2_5_hash = generateRevisionHash(o.doc, o.rev1_4_history); o.rev2_5 = "2-" + o.rev2_5_hash o.rev2_5_history = {"start": 2, "ids": [o.rev2_5_hash, o.rev1_4_hash]}; o.rev2_5_revs_info = clone(o.rev1_4_revs_info); o.rev2_5_revs_info.unshift({"rev": o.rev2_5, "status": "available"}); o.spy(o, "value", {"id": "doc1", "ok": true, "rev": o.rev2_5}, "Put new revision") o.jio.put(o.doc, o.f); o.tick(o); // __/__ // / | \ // 1-1 1-2 1-4 // | | // 2-3 2-5 // putAttachment to inexistent document o.doc = { "_id": "doc2", "_mimetype": "text/plain", "_data": "doc 2 - attachment 1", "_attachment": "attachment1" }; o.rev_hash = generateRevisionHash(o.doc, {"start": 0, "ids": []}); o.rev = "1-" + o.rev_hash; o.spy(o, "value", {"ok": true, "id": "doc2", "attachment": "attachment1", "rev": o.rev}, "Put an attachment to an inexistent document"); o.jio.putAttachment(o.doc, o.f); o.tick(o); // putAttachment o.doc = { "_id": "doc1", "_mimetype": "text/plain", "_data": "doc 1 - attachment 1", "_attachment": "attachment1", "_rev": o.rev2_5 }; o.rev3_6_hash = generateRevisionHash(o.doc, o.rev2_5_history); o.rev3_6 = "3-" + o.rev3_6_hash; o.rev3_6_history = clone(o.rev2_5_history); o.rev3_6_history.start += 1; o.rev3_6_history.ids.unshift(o.rev3_6_hash); o.rev3_6_revs_info = clone(o.rev2_5_revs_info); o.rev3_6_revs_info.unshift({"rev": o.rev3_6, "status": "available"}); o.spy(o, "value", { "ok": true, "id": "doc1", "attachment": "attachment1", "rev": o.rev3_6 }, "Put an attachment to the first document"); o.jio.putAttachment(o.doc, o.f); o.tick(o); // __/__ // / | \ // 1-1 1-2 1-4 // | | // 2-3 2-5 // | // 3-6+a1 // get document o.doc = { "_id": "doc1", "_rev": o.rev3_6, "_revisions": o.rev3_6_history, "_revs_info": o.rev3_6_revs_info, "_conflicts": [o.rev2_3, o.rev1_1], "_attachments": { "attachment1": { "length": "doc 1 - attachment 1".length, "content_type": "text/plain", "digest": "md5-0505c1fb6aae02dd1695d33841726564" } }, "title": "put new revision" }; o.spy(o, "value", o.doc, "Get document, the winner"); o.jio.get({"_id": "doc1"}, { "conflicts": true, "revs": true, "revs_info": true }, o.f); o.tick(o); // get attachment o.doc = { "_id": "doc1", "_attachment": "attachment1" }; o.spy(o, "value", "doc 1 - attachment 1", "Get the winner's attachment"); o.jio.getAttachment(o.doc, o.f); o.tick(o); // put document o.doc = { "_id": "doc1", "_rev": o.rev3_6, "title": "Put revision, attachment must be copied" }; o.rev4_7_hash = generateRevisionHash(o.doc, o.rev3_6_history); o.rev4_7 = "4-" + o.rev4_7_hash; o.rev4_7_history = clone(o.rev3_6_history); o.rev4_7_history.start += 1; o.rev4_7_history.ids.unshift(o.rev4_7_hash); o.rev4_7_revs_info = clone(o.rev3_6_revs_info); o.rev4_7_revs_info.unshift({"rev": o.rev4_7, "status": "available"}); o.spy(o, "value", {"ok": true, "id": "doc1", "rev": o.rev4_7}, "Update document, attachment should be copied"); o.jio.put(o.doc, o.f); o.tick(o); // __/__ // / | \ // 1-1 1-2 1-4 // | | // 2-3 2-5 // | // 3-6+a1 // | // 4-7+a1 // get document, attachment must be copied o.doc = { "_id": "doc1", "_rev": o.rev4_7, "title": o.doc.title, "_attachments": { "attachment1": { "length": "doc 1 - attachment 1".length, "content_type": "text/plain", "digest": "md5-0505c1fb6aae02dd1695d33841726564" } }, "_conflicts": [o.rev2_3, o.rev1_1], "_revisions": o.rev4_7_history, "_revs_info": o.rev4_7_revs_info }; o.spy(o, "value", o.doc, "Get the new winner document and its attachment metadata"); o.jio.get({"_id": "doc1"}, { "conflicts": true, "revs": true, "revs_info": true }, o.f); o.tick(o); // get attachment o.doc = { "_id": "doc1", "_attachment": "attachment1" }; o.spy(o, "value", "doc 1 - attachment 1", "Get the winner's attachment again"); o.jio.getAttachment(o.doc, o.f); o.tick(o); // remove attachment o.doc = { "_id": "doc1", "_attachment": "attachment1", "_rev": o.rev4_7 }; o.rev5_8_hash = generateRevisionHash(o.doc, o.rev4_7_history); o.rev5_8 = "5-" + o.rev5_8_hash; o.rev5_8_history = clone(o.rev4_7_history); o.rev5_8_history.start += 1; o.rev5_8_history.ids.unshift(o.rev5_8_hash); o.rev5_8_revs_info = clone(o.rev4_7_revs_info); o.rev5_8_revs_info.unshift({"rev": o.rev5_8, "status": "available"}); o.spy(o, "value", { "ok": true, "id": "doc1", "attachment": "attachment1", "rev": o.rev5_8 }, "Remove attachment"); o.jio.removeAttachment(o.doc, o.f); o.tick(o); // __/__ // / | \ // 1-1 1-2 1-4 // | | // 2-3 2-5 // | // 3-6+a1 // | // 4-7+a1 // | // 5-8 // get document to check attachment existence o.doc = { "_id": "doc1", "_rev": o.rev5_8, "title": "Put revision, attachment must be copied", "_conflicts": [o.rev2_3, o.rev1_1], "_revisions": o.rev5_8_history, "_revs_info": o.rev5_8_revs_info }; o.spy(o, "value", o.doc, "Get the new winner document, no attachment must be provided"); o.jio.get({"_id": "doc1"}, { "conflicts": true, "revs": true, "revs_info": true }, o.f); o.tick(o); // get specific document o.doc = { "_id": "doc1", "_rev": o.rev4_7, "title": o.doc.title, "_attachments": { "attachment1": { "length": "doc 1 - attachment 1".length, "content_type": "text/plain", "digest": "md5-0505c1fb6aae02dd1695d33841726564" } }, "_conflicts": [o.rev2_3, o.rev1_1], "_revisions": o.rev4_7_history, "_revs_info": o.rev4_7_revs_info }; o.spy(o, "value", o.doc, "Get the new winner document and its attachment metadata"); o.jio.get({"_id": "doc1", "_rev": o.rev4_7}, { "conflicts": true, "revs": true, "revs_info": true }, o.f); o.tick(o); // get inexistent attachment o.spy(o, "status", 404, "Get inexistent winner attachment" + " -> 404 Not Found"); o.jio.get({"_id": "doc1/attachment1"}, o.f); o.tick(o); // get specific attachment o.doc = { "_id": "doc1", "_attachment": "attachment1", "_rev": o.rev3_6 }; o.spy(o, "value", "doc 1 - attachment 1", "Get a specific attachment"); o.jio.getAttachment(o.doc, o.f); o.tick(o); // remove specific document and conflict o.doc = {"_id": "doc1", "_rev": o.rev1_1}; // generate with deleted_flag o.rev2_9_hash = generateRevisionHash(o.doc, o.rev1_1_history, true); o.rev2_9 = "2-" + o.rev2_9_hash; o.rev2_9_history = clone(o.rev1_1_history); o.rev2_9_history.start += 1; o.rev2_9_history.ids.unshift(o.rev2_9_hash); o.rev2_9_revs_info = clone(o.rev1_1_revs_info); o.rev2_9_revs_info.unshift({"rev": o.rev2_9, "status": "deleted"}); o.spy(o, "value", {"ok": true, "id": "doc1", "rev": o.rev2_9}, "Remove specific document, and one conflict"); o.jio.remove(o.doc, o.f); o.tick(o); // __/___ // / | \ // 1-1 1-2 1-4 // | | | // D2-9 2-3 2-5 // | // 3-6+a1 // | // 4-7+a1 // | // 5-8 // remove specific document and conflict o.doc = {"_id": "doc1", "_rev": o.rev2_3}; o.rev3_10_hash = generateRevisionHash(o.doc, o.rev2_3_history, true); o.rev3_10 = "3-" + o.rev3_10_hash; o.rev3_10_history = clone(o.rev2_3_history); o.rev3_10_history.start += 1; o.rev3_10_history.ids.unshift(o.rev3_10_hash); o.rev3_10_revs_info = clone(o.rev2_3_revs_info); o.rev3_10_revs_info.unshift({"rev": o.rev3_10, "status": "deleted"}); o.spy(o, "value", {"ok": true, "id": "doc1", "rev": o.rev3_10}, "Remove specific document, and one conflict"); o.jio.remove(o.doc, o.f); o.tick(o); // ___/____ // / | \ // 1-1 1-2 1-4 // | | | // D2-9 2-3 2-5 // | | // D3-10 3-6+a1 // | // 4-7+a1 // | // 5-8 // get document no more conflict o.doc = { "_id": "doc1", "_rev": o.rev5_8, "title": "Put revision, attachment must be copied", "_revisions": o.rev5_8_history, "_revs_info": o.rev5_8_revs_info }; o.spy(o, "value", o.doc, "Get the new winner document, no more conflicts"); o.jio.get({"_id": "doc1"}, { "conflicts": true, "revs": true, "revs_info": true }, o.f); o.tick(o); // remove document o.doc = { "_id": "doc1", "_rev": o.rev5_8 }; o.rev6_11_hash = generateRevisionHash(o.doc, o.rev5_8_history, true); o.rev6_11 = "6-" + o.rev6_11_hash; o.spy(o, "value", {"ok": true, "id": "doc1", "rev": o.rev6_11}, "Remove the last document"); o.jio.remove(o.doc, o.f); o.tick(o); // ___/____ // / | \ // 1-1 1-2 1-4 // | | | // D2-9 2-3 2-5 // | | // D3-10 3-6+a1 // | // 4-7+a1 // | // 5-8 // | // D6-11 // get inexistent document o.spy(o, "status", 404, "Get inexistent document -> 404 Not Found"); o.jio.get({"_id": "doc3"}, { "conflicts": true, "revs": true, "revisions": true }, o.f); o.tick(o); // get specific deleted document o.spy(o, "status", 404, "Get deleted document -> 404 Not Found"); o.jio.get({"_id": "doc1", "rev": o.rev3_10}, { "conflicts": true, "revs": true, "revs_info": true }, o.f); o.tick(o); // get specific deleted document o.spy(o, "status", 404, "Get deleted document -> 404 Not Found"); o.jio.get({"_id": "doc1"}, { "conflicts": true, "revs": true, "revs_info": true }, o.f); o.tick(o); o.jio.stop(); }; test ("[Revision + Local Storage] Scenario", function () { testReplicateRevisionStorage(this, { "type": "replicaterevision", "storage_list": [{ "type": "revision", "sub_storage": { "type": "local", "username": "ureprevloc", "application_name": "areprevloc" } }] }); }); test("[Replicate Revision + Revision + Local Storage] Scenario", function () { testReplicateRevisionStorage(this, { "type": "replicaterevision", "storage_list": [{ "type": "replicaterevision", "storage_list": [{ "type": "revision", "sub_storage": { "type": "local", "username": "urepreprevloc", "application_name": "arepreprevloc" } }] }] }); }); test ("2x [Revision + Local Storage] Scenario", function () { testReplicateRevisionStorage(this, { "type": "replicaterevision", "storage_list": [{ "type": "revision", "sub_storage": { "type": "local", "username": "ureprevlocloc1", "application_name": "areprevlocloc1" } }, { "type": "revision", "sub_storage": { "type": "local", "username": "ureprevlocloc2", "application_name": "areprevlocloc2" } }] }); }); test("2x [Replicate Rev + 2x [Rev + Local]] Scenario", function () { testReplicateRevisionStorage(this, { "type": "replicaterevision", "storage_list": [{ "type": "replicaterevision", "storage_list": [{ "type": "revision", "sub_storage": { "type": "local", "username": "urepreprevloc1", "application_name": "arepreprevloc1" } }, { "type": "revision", "sub_storage": { "type": "local", "username": "urepreprevloc2", "application_name": "arepreprevloc2" } }] }, { "type": "replicaterevision", "storage_list": [{ "type": "revision", "sub_storage": { "type": "local", "username": "urepreprevloc3", "application_name": "arepreprevloc3" } }, { "type": "revision", "sub_storage": { "type": "local", "username": "urepreprevloc4", "application_name": "arepreprevloc4" } }] }] }); }); var replicateStorageSynchronisationGenerator = function ( that, description, index ) { var o = generateTools(that); o.jio = JIO.newJio(description); o.localpath1 = "jio/localstorage/usyncreprevlocloc1/" + index; o.localpath2 = "jio/localstorage/usyncreprevlocloc2/" + index; o.localpath3 = "jio/localstorage/usyncreprevlocloc3/" + index; o.localpath4 = "jio/localstorage/usyncreprevlocloc4/" + index; // add documents to localstorage o.doctree1_1 = { "_id": "doc1.revision_tree.json", "children": [{ "rev": "1-111", "status": "available", "children": [], }] }; o.doc1_1 = {"_id": "doc1.1-111", "title": "A"}; localstorage.setItem(o.localpath1 + "/doc1.revision_tree.json", o.doctree1_1); localstorage.setItem(o.localpath2 + "/doc1.revision_tree.json", o.doctree1_1); localstorage.setItem(o.localpath3 + "/doc1.revision_tree.json", o.doctree1_1); localstorage.setItem(o.localpath4 + "/doc1.revision_tree.json", o.doctree1_1); localstorage.setItem(o.localpath1 + "/" + o.doc1_1._id, o.doc1_1); localstorage.setItem(o.localpath2 + "/" + o.doc1_1._id, o.doc1_1); localstorage.setItem(o.localpath3 + "/" + o.doc1_1._id, o.doc1_1); localstorage.setItem(o.localpath4 + "/" + o.doc1_1._id, o.doc1_1); // no synchronisation o.spy(o, "value", {"ok": true, "id": "doc1"}, "Check document"); o.jio.check({"_id": "doc1"}, o.f); o.tick(o); o.spy(o, "value", {"ok": true, "id": "doc1", "rev": "1-111"}, "Check document with revision"); o.jio.check({"_id": "doc1", "_rev": "1-111"}, o.f); o.tick(o); o.spy(o, "value", {"ok": true, "id": "doc1"}, "Repair document"); o.jio.repair({"_id": "doc1"}, o.f); o.tick(o); // check documents from localstorage deepEqual( localstorage.getItem(o.localpath1 + "/doc1.revision_tree.json"), o.doctree1_1, "Check revision tree 1, no synchro done" ); deepEqual( localstorage.getItem(o.localpath2 + "/doc1.revision_tree.json"), o.doctree1_1, "Check revision tree 2, no synchro done" ); deepEqual( localstorage.getItem(o.localpath3 + "/doc1.revision_tree.json"), o.doctree1_1, "Check revision tree 3, no synchro done" ); deepEqual( localstorage.getItem(o.localpath4 + "/doc1.revision_tree.json"), o.doctree1_1, "Check revision tree 4, no synchro done" ); // add documents to localstorage o.doctree2_2 = clone(o.doctree1_1); o.doctree2_2.children[0].children.push({ "rev": "2-222", "status": "available", "children": [] }); o.doc2_2 = { "_id": "doc1.2-222", "title": "B", "_attachments": { "haha": { "length": 3, "digest": "md5-900150983cd24fb0d6963f7d28e17f72", "content_type": "text/plain" } } }; localstorage.setItem(o.localpath1 + "/doc1.revision_tree.json", o.doctree2_2); localstorage.setItem(o.localpath1 + "/" + o.doc2_2._id, o.doc2_2) localstorage.setItem(o.localpath1 + "/" + o.doc2_2._id + "/haha", "abc"); // document synchronisation without conflict o.spy(o, "status", 41, "Check document"); o.jio.check({"_id": "doc1"}, o.f); o.tick(o, 50000); o.spy(o, "value", {"ok": true, "id": "doc1"}, "Repair document"); o.jio.repair({"_id": "doc1"}, o.f); o.tick(o, 50000); // check documents from localstorage deepEqual( localstorage.getItem(o.localpath1 + "/doc1.revision_tree.json"), o.doctree2_2, "Check revision tree 1, no synchro done" ); deepEqual( localstorage.getItem(o.localpath2 + "/doc1.revision_tree.json"), o.doctree2_2, "Check revision tree 2, revision synchro done" ); deepEqual( localstorage.getItem(o.localpath3 + "/doc1.revision_tree.json"), o.doctree2_2, "Check revision tree 3, revision synchro done" ); deepEqual( localstorage.getItem(o.localpath3 + "/doc1.2-222"), o.doc2_2, "Check document 3" ); deepEqual( localstorage.getItem(o.localpath3 + "/doc1.2-222/haha"), "abc", "Check attachment 3" ); deepEqual( localstorage.getItem(o.localpath4 + "/doc1.revision_tree.json"), o.doctree2_2, "Check revision tree 4, revision synchro done" ); // add documents to localstorage o.doctree2_3 = clone(o.doctree2_2); o.doctree2_3.children[0].children.unshift({ "rev": "2-223", "status": "available", "children": [] }); o.doc2_3 = {"_id": "doc1.2-223", "title": "C"}; localstorage.setItem(o.localpath1 + "/doc1.revision_tree.json", o.doctree2_3); localstorage.setItem(o.localpath1 + "/" + o.doc2_3._id, o.doc2_3); // document synchronisation with conflict o.spy(o, "status", 41, "Check document"); o.jio.check({"_id": "doc1"}, o.f); o.tick(o, 50000); o.spy(o, "value", {"ok": true, "id": "doc1"}, "Repair document"); o.jio.repair({"_id": "doc1"}, o.f); o.tick(o, 50000); // check documents from localstorage deepEqual( localstorage.getItem(o.localpath1 + "/doc1.revision_tree.json"), o.doctree2_3, "Check revision tree 1, rev synchro" ); deepEqual( localstorage.getItem(o.localpath2 + "/doc1.revision_tree.json"), o.doctree2_3, "Check revision tree 2, rev synchro" ); deepEqual( localstorage.getItem(o.localpath3 + "/doc1.revision_tree.json"), o.doctree2_3, "Check revision tree 3, rev synchro" ); deepEqual( localstorage.getItem(o.localpath3 + "/doc1.2-223"), o.doc2_3, "Check document 3" ); deepEqual( localstorage.getItem(o.localpath4 + "/doc1.revision_tree.json"), o.doctree2_3, "Check revision tree 4, rev synchro" ); o.jio.stop(); }; test("Storage Synchronisation (Repair) 4x [Rev + Local]", function () { replicateStorageSynchronisationGenerator(this, { "type": "replicaterevision", "storage_list": [{ "type": "revision", "sub_storage": { "type": "local", "username": "usyncreprevlocloc1", "application_name": "1" } }, { "type": "revision", "sub_storage": { "type": "local", "username": "usyncreprevlocloc2", "application_name": "1" } }, { "type": "revision", "sub_storage": { "type": "local", "username": "usyncreprevlocloc3", "application_name": "1" } }, { "type": "revision", "sub_storage": { "type": "local", "username": "usyncreprevlocloc4", "application_name": "1" } }] }, "1"); }); test("Storage Synchronisation (Repair) 2x [Rep 2x [Rev + Local]]", function () { replicateStorageSynchronisationGenerator(this, { "type": "replicaterevision", "storage_list": [{ "type": "replicaterevision", "storage_list": [{ "type": "revision", "sub_storage": { "type": "local", "username": "usyncreprevlocloc1", "application_name": "2" } }, { "type": "revision", "sub_storage": { "type": "local", "username": "usyncreprevlocloc2", "application_name": "2" } }] }, { "type": "replicaterevision", "storage_list": [{ "type": "revision", "sub_storage": { "type": "local", "username": "usyncreprevlocloc3", "application_name": "2" } }, { "type": "revision", "sub_storage": { "type": "local", "username": "usyncreprevlocloc4", "application_name": "2" } }] }] }, "2"); }); module ("Jio DAVStorage"); test ("Post", function () { var o = generateTools(this); o.jio = JIO.newJio({ "type": "dav", "url": "https://ca-davstorage:8080", "auth_type": "basic", "username": "admin", "password": "pwd" }); // post without id o.server = sinon.fakeServer.create(); o.server.respondWith( "GET", /https:\/\/ca-davstorage:8080\/[0-9a-fA-F]{4}/, [ 404, {"Content-Type": "text/html"}, "<h1>Document not found</h1>" ] ); o.server.respondWith( "PUT", /https:\/\/ca-davstorage:8080\/[0-9a-fA-F]{4}/, [ 200, {"Content-Type": "text/html"}, "<h1>Document updated!</h1>" ] ); o.spy(o, "jobstatus", "done", "Post without id"); o.jio.post({}, {"max_retry": 1}, function (err, response) { o.f.apply(arguments); if (response) { ok(isUuid(response.id), "Uuid should look like " + "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx : " + response.id); } }); o.clock.tick(1000); o.server.respond(); o.tick(o); o.server.restore(); // post document with id o.server = sinon.fakeServer.create(); o.server.respondWith( "GET", /https:\/\/ca-davstorage:8080\/http:%252F%252F100%2525_\.json/, [ 404, {"Content-Type": "text/html"}, "<h1>Document not found</h1>" ] ); o.server.respondWith( "PUT", /https:\/\/ca-davstorage:8080\/http:%252F%252F100%2525_\.json/, [ 200, {"Content-Type": "text/html"}, "<h1>Document updated!</h1>" ] ); o.spy(o, "value", {"id": "http://100%.json", "ok": true}, "Create document with an id"); o.jio.post({ "_id": "http://100%.json", "title": "Hello There" }, {"max_retry": 1}, o.f); o.clock.tick(1000); o.server.respond(); o.tick(o); o.server.restore(); // post already existant file o.server = sinon.fakeServer.create(); o.server.respondWith( "GET", /https:\/\/ca-davstorage:8080\/http:%252F%252F100%2525_\.json/, [ 200, {"Content-Type": "text/plain"}, '{"_id":"doc1","title":"Hello There"}' ] ); o.spy(o, "status", 405, "Update document previous -> 405"); o.jio.post({ "_id": "http://100%.json", "title": "Hello There Again" }, {"max_retry": 1}, o.f); o.clock.tick(1000); o.server.respond(); o.tick(o); o.server.restore(); o.jio.stop(); }); test ("Put", function(){ var o = generateTools(this); o.jio = JIO.newJio({ "type": "dav", "url": "https://ca-davstorage:8080", "auth_type": "basic", "username": "admin", "password": "pwd" }); // put without id => 20 Id Required o.spy (o, "status", 20, "Put without id -> 20"); o.jio.put({}, o.f); o.tick(o); // put non empty document o.server = sinon.fakeServer.create(); o.server.respondWith( "PUT", /https:\/\/ca-davstorage:8080\/http:%252F%252F100%2525_\.json/, [ 200, {"Content-Type": "text/html"}, "<h1>OK1</h1>" ] ); o.spy (o, "value", {"ok": true, "id": "http://100%.json"}, "Create document"); o.jio.put({ "_id": "http://100%.json", "title": "Hi There" }, {"max_retry": 1}, o.f); o.clock.tick(1000); o.server.respond(); o.tick(o); o.server.restore(); // update document o.server = sinon.fakeServer.create(); o.server.respondWith( "PUT", /https:\/\/ca-davstorage:8080\/http:%252F%252F100%2525_\.json/, [ 200, {"Content-Type": "text/html"}, "<h1>OK!</h1>" ] ); o.spy (o, "value", {"ok": true, "id": "http://100%.json"}, "Update document"); o.jio.put({ "_id": "http://100%.json", "title": "Hi There Again" }, {"max_retry": 1}, o.f); o.clock.tick(1000); o.server.respond(); o.tick(o); o.server.restore(); // putAttachment without document id => 20 Id Required o.spy(o, "status", 20, "PutAttachment without doc id -> 20"); o.jio.putAttachment({"_attachment": "body.html"}, o.f); o.tick(o); // putAttachment without attachment id => 22 Attachment Id Required o.spy(o, "status", 22, "PutAttachment without attachment id -> 22"); o.jio.putAttachment({"_id": "http://100%.json"}, o.f); o.tick(o); // putAttachment without underlying document => 404 Not Found o.server = sinon.fakeServer.create(); o.server.respondWith( "GET", /https:\/\/ca-davstorage:8080\/http:%252F%252F100%2525_\.json/, [ 404, {"Content-Type": "text/html"}, "<h1>Not Found</h1>" ] ); o.spy(o, "status", 404, "PutAttachment without document -> 404"); o.jio.putAttachment({ "_id": "http://100%.json", "_attachment": "putattmt2" }, {"max_retry": 1}, o.f); o.clock.tick(1000); o.server.respond(); o.tick(o); o.server.restore(); // upload attachment o.server = sinon.fakeServer.create(); o.server.respondWith( "GET", /https:\/\/ca-davstorage:8080\/http:%252F%252F100%2525_\.json/, [ 200, {"Content-Type": "text/plain"}, '{"_id":"http://100%.json","title":"Hi There!"}' ] ); o.server.respondWith( "PUT", /https:\/\/ca-davstorage:8080\/http:%252F%252F100%2525_\.json.body_\.html/, [ 200, {"Content-Type": "text/html"}, "<h1>OK!</h1>" ] ); o.server.respondWith( "PUT", /https:\/\/ca-davstorage:8080\/http:%252F%252F100%2525_\.json/, [ 200, {"Content-Type": "text/html"}, "<h1>OK!</h1>" ] ); o.spy(o, "value", { "ok": true, "id": "http://100%.json", "attachment": "body.html" }, "Upload attachment"); o.jio.putAttachment({ "_id": "http://100%.json", "_attachment": "body.html", "_mimetype": "text/html", "_data": "<h1>Hi There!!</h1><p>How are you?</p>" }, {"max_retry": 1}, o.f); o.clock.tick(1000); o.server.respond(); o.tick(o); o.server.restore(); o.jio.stop(); }); test ("Get", function(){ var o = generateTools(this); o.jio = JIO.newJio({ "type": "dav", "url": "https://ca-davstorage:8080", "auth_type": "basic", "username": "admin", "password": "pwd" }); // get inexistent document o.server = sinon.fakeServer.create(); o.server.respondWith( "GET", /https:\/\/ca-davstorage:8080\/http:%252F%252F100%2525_\.json/, [ 404, {"Content-Type": "text/html"}, "<h1>Not Found</h1>" ] ); o.spy(o, "status", 404, "Get non existing document -> 404"); o.jio.get({"_id": "http://100%.json"}, {"max_retry": 1}, o.f); o.clock.tick(1000); o.server.respond(); o.tick(o); o.server.restore(); // get document o.server = sinon.fakeServer.create(); o.server.respondWith( "GET", /https:\/\/ca-davstorage:8080\/http:%252F%252F100%2525_\.json/, [ 200, {"Content-Type": "text/html"}, '{"_id":"http://100%.json","title":"Hi There!"}' ] ); o.spy(o, "value", {"_id": "http://100%.json", "title": "Hi There!"}, "Get document"); o.jio.get({"_id": "http://100%.json"}, {"max_retry": 1}, o.f); o.clock.tick(1000); o.server.respond(); o.tick(o); o.server.restore(); // get inexistent attachment o.server = sinon.fakeServer.create(); o.server.respondWith( "GET", /https:\/\/ca-davstorage:8080\/http:%252F%252F100%2525_\.json\.body_\.html/, [ 404, {"Content-Type": "text/html"}, "<h1>Not Found</h1>" ] ); o.spy(o, "status", 404, "Get inexistent attachment -> 404"); o.jio.getAttachment({ "_id": "http://100%.json", "_attachment": "body.html" }, {"max_retry": 1}, o.f); o.clock.tick(1000); o.server.respond(); o.tick(o); o.server.restore(); // get attachment o.server = sinon.fakeServer.create(); o.server.respondWith( "GET", /https:\/\/ca-davstorage:8080\/http:%252F%252F100%2525_\.json\.body_\.html/, [ 200, {"Content-Type": "text/plain"}, "My Attachment Content" ] ); o.spy(o, "value", "My Attachment Content", "Get attachment"); o.jio.getAttachment({ "_id": "http://100%.json", "_attachment": "body.html" }, {"max_retry": 1}, o.f); o.clock.tick(1000); o.server.respond(); o.tick(o); o.server.restore(); o.jio.stop(); }); test ("Remove", function(){ var o = generateTools(this); o.jio = JIO.newJio({ "type": "dav", "url": "https://ca-davstorage:8080", "auth_type": "basic", "username": "admin", "password": "pwd" }); // remove inexistent document o.server = sinon.fakeServer.create(); o.server.respondWith( "GET", /https:\/\/ca-davstorage:8080\/http:%252F%252F100%2525_\.json/, [ 404, {"Content-Type": "text/html"}, "<h1>Not Found</h1>" ] ); o.spy(o, "status", 404, "Remove inexistent document -> 404"); o.jio.remove({"_id": "http://100%.json"}, {"max_retry": 1}, o.f); o.clock.tick(1000); o.server.respond(); o.tick(o); o.server.restore(); // remove document o.server = sinon.fakeServer.create(); o.server.respondWith( "GET", /https:\/\/ca-davstorage:8080\/http:%252F%252F100%2525_\.json/, [ 200, {"Content-Type": "text/html"}, "{My corrupted document}" ] ); o.server.respondWith( "DELETE", /https:\/\/ca-davstorage:8080\/http:%252F%252F100%2525_\.json/, [ 200, {"Content-Type": "text/plain"}, "<h1>Deleted</h1>" ] ); o.spy(o, "value", {"ok": true, "id": "http://100%.json"}, "Remove document"); o.jio.remove({"_id": "http://100%.json"}, {"max_retry": 1}, o.f); o.clock.tick(1000); o.server.respond(); o.tick(o); o.server.restore(); // remove inexistent attachment o.server = sinon.fakeServer.create(); o.server.respondWith( "GET", /https:\/\/ca-davstorage:8080\/http:%252F%252F100%2525_\.json/, [ 200, {"Content-Type": "text/plain"}, "{}" ] ); o.spy(o, "status", 404, "Remove inexistent attachment -> 404"); o.jio.removeAttachment({ "_id": "http://100%.json", "_attachment": "body.html" }, {"max_retry": 1}, o.f); o.clock.tick(1000); o.server.respond(); o.tick(o); // o.server.respond(); // o.server.respond(); o.server.restore(); // remove attachment o.server = sinon.fakeServer.create(); o.server.respondWith( "GET", /https:\/\/ca-davstorage:8080\/http:%252F%252F100%2525_\.json/, [ 200, {"Content-Type": "text/plain"}, JSON.stringify({ "_attachments": { "body.html": { "length": 32, "digest": "md5-dontcare", "content_type": "text/html" } } }) ] ); o.server.respondWith( "PUT", /https:\/\/ca-davstorage:8080\/http:%252F%252F100%2525_\.json/, [ 200, {"Content-Type": "text/html"}, "<h1>OK</h1>" ] ); o.server.respondWith( "DELETE", /https:\/\/ca-davstorage:8080\/http:%252F%252F100%2525_\.json.body_\.html/, [ 200, {"Content-Type": "text/html"}, "<h1>OK</h1>" ] ); o.spy(o, "value", { "ok": true, "id": "http://100%.json", "attachment": "body.html" }, "Remove attachment"); o.jio.removeAttachment({ "_id": "http://100%.json", "_attachment": "body.html" }, {"max_retry": 1}, o.f); o.clock.tick(1000); o.server.respond(); o.tick(o); o.server.restore(); // remove document with multiple attachments o.server = sinon.fakeServer.create(); o.server.respondWith( "GET", /https:\/\/ca-davstorage:8080\/http:%252F%252F100%2525_\.json/, [ 200, {"Content-Type": "text/html"}, JSON.stringify({ "_attachments": { "body.html": { "length": 32, "digest": "md5-dontcare", "content_type": "text/html" }, "other": { "length": 3, "digest": "md5-dontcare-again", "content_type": "text/plain" } } }) ] ); o.server.respondWith( "DELETE", /https:\/\/ca-davstorage:8080\/http:%252F%252F100%2525_\.json/, [ 200, {"Content-Type": "text/plain"}, "<h1>Deleted</h1>" ] ); o.server.respondWith( "DELETE", /https:\/\/ca-davstorage:8080\/http:%252F%252F100%2525_\.json\.body_\.html/, [ 200, {"Content-Type": "text/plain"}, "<h1>Deleted</h1>" ] ); o.server.respondWith( "DELETE", /https:\/\/ca-davstorage:8080\/http:%252F%252F100%2525_\.json\.other/, [ 200, {"Content-Type": "text/plain"}, "<h1>Deleted</h1>" ] ); o.spy(o, "value", {"ok": true, "id": "http://100%.json"}, "Remove document containing multiple attachments"); o.jio.remove({"_id": "http://100%.json"}, {"max_retry": 1}, o.f); o.clock.tick(1000); o.server.respond(); o.tick(o); o.server.restore(); o.jio.stop(); }); test ("AllDocs", function () { // need to make server requests before activating fakeServer var davlist = getXML('responsexml/davlist'), o = generateTools(this); o.jio = JIO.newJio({ "type": "dav", "url": "https://ca-davstorage:8080", "auth_type": "basic", "username": "admin", "password": "pwd" }); // get all documents o.server = sinon.fakeServer.create(); o.server.respondWith( "PROPFIND", /https:\/\/ca-davstorage:8080\//, [ 200, {"Content-Type": "text/xml"}, davlist ] ); o.spy(o, "value", { "rows": [ {"id": "http://100%.json", "key": "http://100%.json", "value": {}}, {"id": "ISBN:1038729410372", "key": "ISBN:1038729410372", "value": {}} ], "total_rows": 2 }, "allDocs"); o.jio.allDocs(o.f); o.clock.tick(1000); o.server.respond(); o.tick(o); o.server.restore(); // allDocs with option include_docs o.server = sinon.fakeServer.create(); o.server.respondWith( "PROPFIND", /https:\/\/ca-davstorage:8080\//, [ 200, {"Content-Type": "text/xml"}, davlist ] ); o.doc1 = {"_id": "http://100%.json", "_attachments": { "body.html": { "length": 32, "digest": "md5-doncare", "content_type": "text/html" } }}; o.doc2 = {"_id": "ISBN:1038729410372", "title": "Book Title"}; o.server.respondWith( "GET", /https:\/\/ca-davstorage:8080\/http:%252F%252F100%2525_\.json/, [ 200, {"Content-Type": "text/plain"}, JSON.stringify(o.doc1) ] ); o.server.respondWith( "GET", /https:\/\/ca-davstorage:8080\/ISBN:1038729410372/, [ 200, {"Content-Type": "text/plain"}, JSON.stringify(o.doc2) ] ); o.spy(o, "value", { "rows": [{ "id": "http://100%.json", "key": "http://100%.json", "value": {}, "doc": o.doc1 }, { "id": "ISBN:1038729410372", "key": "ISBN:1038729410372", "value": {}, "doc": o.doc2 }], "total_rows": 2 }, "allDocs (include_docs)"); o.jio.allDocs({"include_docs": true}, o.f); o.clock.tick(1000); o.server.respond(); o.tick(o); o.server.restore(); o.jio.stop(); }); // NOTES: this test is for a live webDav server on localstorage // see the documentation how to setup an apache2 webDav-server // tests cannot be run subsequently, so only do one test at a time /* test ("webDav Live Server setup", function () { var o = generateTools(this); // turn off fakeserver - otherwise no requests will be made o.server.restore(); o.jio = JIO.newJio({ "type": "dav", "username": "davlive", "password": "checkpwd", "url": "http://127.0.1.1/dav" }); // not used, check console for responses // o.spy(o, "value", {"id": "_id_", "ok": true}, "Live Webdav"); // post a new document o.jio.post({"_id": "one.json", "title": "hello"}), o.f); o.clock.tick(5000); // modify document o.jio.put({"_id": "one.json", "title": "hello modified"}), o.f); o.clock.tick(5000); // add attachment o.jio.putAttachment({ "id": "one.json/att.txt", "mimetype": "text/plain", "content":"there2" }, o.f); // test allDocs o.jio.allDocs({"include_docs":true}, function(s){console.log(s);}, function ( e ) {console.log(e); }, o.f); o.clock.tick(5000); // get Attachment o.jio.get("one.json/att.txt", o.f); o.clock.tick(5000); // remove Attachment o.jio.remove("one.json/att.txt", o.f.); o.clock.tick(5000); // remove Document o.jio.remove("one.json", o.f.); o.clock.tick(5000); o.jio.stop(); }); */ /* module ('Jio ReplicateStorage'); test ('Document load', function () { // Test if ReplicateStorage can load several documents. var o = {}; o.clock = this.sandbox.useFakeTimers(); o.t = this; o.clock.tick(base_tick); o.mytest = function (message,doc,doc2) { o.f = function (err,val) { var gooddoc = doc; if (val) { if (doc2 && val.content === doc2.content) { gooddoc = doc2; } } deepEqual (err || val,gooddoc,message); }; o.t.spy(o,'f'); o.jio.get('file',{max_retry:3},o.f); o.clock.tick(10000); if (!o.f.calledOnce) { if (o.f.called) { ok(false, 'too much results'); } else { ok(false, 'no response'); } } }; o.jio = JIO.newJio({type:'replicate',storagelist:[ {type:'dummyallok',username:'1'}, {type:'dummyallok',username:'2'}]}); o.mytest('DummyStorageAllOK,OK: load same file',{ _id:'file',content:'content', _last_modified:15000, _creation_date:10000 }); o.jio.stop(); o.jio = JIO.newJio({type:'replicate',storagelist:[ {type:'dummyall3tries'}, {type:'dummyallok'}]}); o.mytest('DummyStorageAllOK,3tries: load 2 different files', { _id:'file',content:'content', _last_modified:15000,_creation_date:10000 },{ _id:'file',content:'content file', _last_modified:17000,_creation_date:11000 }); o.jio.stop(); }); test ('Document save', function () { // Test if ReplicateStorage can save several documents. var o = {}; o.clock = this.sandbox.useFakeTimers(); o.t = this; o.clock.tick(base_tick); o.mytest = function (message,value) { o.f = function (err,val) { if (err) { err = err.status; } deepEqual (err || val,value,message); }; o.t.spy(o,'f'); o.jio.put({_id:'file',content:'content'},{max_retry:3},o.f); o.clock.tick(500); if (!o.f.calledOnce) { if (o.f.called) { ok(false, 'too much results'); } else { ok(false, 'no response'); } } }; o.jio = JIO.newJio({type:'replicate',storagelist:[ {type:'dummyallok',username:'1'}, {type:'dummyallok',username:'2'}]}); o.mytest('DummyStorageAllOK,OK: save a file.',{ok:true,id:'file'}); o.jio.stop(); o.jio = JIO.newJio({type:'replicate',storagelist:[ {type:'dummyall3tries',username:'1'}, {type:'dummyallok',username:'2'}]}); o.mytest('DummyStorageAll3Tries,OK: save a file.',{ok:true,id:'file'}); o.jio.stop(); }); test ('Get Document List', function () { // Test if ReplicateStorage can get several list. var o = {}; o.clock = this.sandbox.useFakeTimers(); o.t = this; o.clock.tick(base_tick); o.mytest = function (message,value) { o.f = function (err,val) { deepEqual (err || objectifyDocumentArray(val.rows), objectifyDocumentArray(value),message); }; o.t.spy(o,'f'); o.jio.allDocs({max_retry:3},o.f); o.clock.tick(10000); if (!o.f.calledOnce) { if (o.f.called) { ok(false, 'too much results'); } else { ok(false, 'no response'); } } }; o.jio = JIO.newJio({type:'replicate',storagelist:[ {type:'dummyall3tries',username:'1'}, {type:'dummyallok',username:'2'}]}); o.doc1 = {id:'file',key:'file',value:{ _last_modified:15000,_creation_date:10000}}; o.doc2 = {id:'memo',key:'memo',value:{ _last_modified:25000,_creation_date:20000}}; o.mytest('DummyStorageAllOK,3tries: get document list.', [o.doc1,o.doc2]); o.jio.stop(); o.jio = JIO.newJio({type:'replicate',storagelist:[ {type:'dummyall3tries',username:'3'}, {type:'dummyall3tries',username:'4'}]}); o.mytest('DummyStorageAll3tries,3tries: get document list.', [o.doc1,o.doc2]); o.jio.stop(); }); test ('Remove document', function () { // Test if ReplicateStorage can remove several documents. var o = {}; o.clock = this.sandbox.useFakeTimers(); o.t = this; o.clock.tick(base_tick); o.mytest = function (message,value) { o.f = function (err,val) { if (err) { err = err.status; } deepEqual (err || val,value,message); }; o.t.spy(o,'f'); o.jio.remove({_id:'file'},{max_retry:3},o.f); o.clock.tick(10000); if (!o.f.calledOnce) { if (o.f.called) { ok(false, 'too much results'); } else { ok(false, 'no response'); } } }; o.jio = JIO.newJio({type:'replicate',storagelist:[ {type:'dummyallok',username:'1'}, {type:'dummyall3tries',username:'2'}]}); o.mytest('DummyStorageAllOK,3tries: remove document.',{ok:true,id:'file'}); o.jio.stop(); }); */ module ("Jio IndexStorage"); test ("Post", function () { var o = generateTools(this); o.jio = JIO.newJio({ "type": "indexed", "indices": [ {"id": "A", "index": ["title"]}, {"id": "B", "index": ["title", "year"]} ], "sub_storage": { "type": "local", "username": "ipost", "application_name": "ipost" } }); // post without id o.spy (o, "jobstatus", "done", "Post without id"); o.jio.post({}, function (err, response) { o.id = (response || {}).id; o.f(err, response); }); o.tick(o); // post non empty document o.doc = {"_id": "some_id", "title": "My Title", "year": 2000, "hey": "def"}; o.spy (o, "value", {"ok": true, "id": "some_id"}, "Post document"); o.jio.post(o.doc, o.f); o.tick(o); // check document o.fakeIndexA = { "_id": "A", "indexing": ["title"], "free": [], "location": { "some_id": 0 }, "database": [ {"_id": "some_id", "title": "My Title"} ] }; o.spy(o, "value", o.fakeIndexA, "Check index file"); o.jio.get({"_id": "A"}, o.f); o.tick(o); o.fakeIndexB = { "_id": "B", "indexing": ["title", "year"], "free": [], "location": { "some_id": 0 }, "database": [ {"_id": "some_id", "title": "My Title", "year": 2000} ] }; o.spy(o, "value", o.fakeIndexB, "Check index file"); o.jio.get({"_id": "B"}, o.f); o.tick(o); // post with escapable characters o.doc = {"_id": "other_id", "title": "myPost2", "findMeA":"keyword_*§$%&/()=?", "findMeB":"keyword_|ð@ł¶đæðſæðæſ³" }; o.spy (o, "value", {"ok": true, "id": "other_id"}, "Post with escapable characters"); o.jio.post(o.doc, o.f); o.tick(o); // post and document already exists o.doc = {"_id": "some_id", "title": "myPost3", "findMeA":"keyword_ghi", "findMeB":"keyword_jkl" } o.spy (o, "status", 409, "Post and document already exists"); o.jio.post(o.doc, o.f); o.tick(o); o.jio.stop(); }); test ("Put", function(){ var o = generateTools(this); o.jio = JIO.newJio({ "type": "indexed", "indices": [ {"id": "A", "index": ["author"]}, {"id": "B", "index": ["year"]} ], "sub_storage": { "type": "local", "username": "iput", "application_name": "iput" } }); // put without id // error 20 -> document id required o.spy (o, "status", 20, "Put without id"); o.jio.put({}, o.f); o.tick(o); // put non empty document o.doc = {"_id": "put1", "title": "myPut1", "author": "John Doe"}; o.spy (o, "value", {"ok": true, "id": "put1"}, "Put-create document"); o.jio.put(o.doc, o.f); o.tick(o); // check index file o.fakeIndexA = { "_id": "A", "indexing": ["author"], "free": [], "location": { "put1": 0 }, "database": [{"_id": "put1", "author": "John Doe"}] }; o.spy(o, "value", o.fakeIndexA, "Check index file"); o.jio.get({"_id": "A"}, o.f); o.tick(o); o.fakeIndexB = { "_id": "B", "indexing": ["year"], "free": [], "location": {}, "database": [] }; o.spy(o, "value", o.fakeIndexB, "Check index file"); o.jio.get({"_id": "B"}, o.f); o.tick(o); // modify document - modify keyword on index! o.doc = {"_id": "put1", "title": "myPuttter1", "author": "Jane Doe"}; o.spy (o, "value", {"ok": true, "id": "put1"}, "Modify existing document"); o.jio.put(o.doc, o.f); o.tick(o); // check index file o.fakeIndexA.database[0].author = "Jane Doe"; o.spy(o, "value", o.fakeIndexA, "Check index file"); o.jio.get({"_id": "A"}, o.f); o.tick(o); // add new document with same keyword! o.doc = {"_id": "new_doc", "title": "myPut2", "author": "Jane Doe"}; o.spy (o, "value", {"ok": true, "id": "new_doc"}, "Add new document with same keyword"); o.jio.put(o.doc, o.f); o.tick(o); // check index file o.fakeIndexA.location.new_doc = 1; o.fakeIndexA.database.push({"_id": "new_doc", "author": "Jane Doe"}); o.spy(o, "value", o.fakeIndexA, "Check index file"); o.jio.get({"_id": "A"}, o.f); o.tick(o); // add second keyword to index file o.doc = {"_id": "put1", "title": "myPut2", "author": "Jane Doe", "year":"1912"}; o.spy (o, "value", {"ok": true, "id": "put1"}, "add second keyword to index file"); o.jio.put(o.doc, o.f); o.tick(o); // check index file o.spy(o, "value", o.fakeIndexA, "Check index file"); o.jio.get({"_id": "A"}, o.f); o.tick(o); o.fakeIndexB.location.put1 = 0; o.fakeIndexB.database.push({"_id": "put1", "year": "1912"}); o.spy(o, "value", o.fakeIndexB, "Check index file"); o.jio.get({"_id": "B"}, o.f); o.tick(o); // remove a keyword from an existing document o.doc = {"_id": "new_doc", "title": "myPut2"}; o.spy (o, "value", {"ok": true, "id": "new_doc"}, "Remove keyword from existing document"); o.jio.put(o.doc, o.f); o.tick(o); // check index file delete o.fakeIndexA.location.new_doc; o.fakeIndexA.database[1] = null; o.fakeIndexA.free.push(1); o.spy(o, "value", o.fakeIndexA, "Check index file"); o.jio.get({"_id": "A"}, o.f); o.tick(o); o.jio.stop(); }); test("Check & Repair", function () { var o = generateTools(this), i; o.jio = JIO.newJio({ "type": "indexed", "indices": [ {"id": "A", "index": ["director"]}, {"id": "B", "index": ["year"]} ], "sub_storage": { "type": "local", "username": "indexstoragerepair" } }); o.fakeIndexA = { "_id": "A", "indexing": ["director"], "free": [], "database": [] }; o.fakeIndexB = { "_id": "B", "indexing": ["year"], "free": [], "database": [] }; for (i = 0; i < 10; i += 1) { o.jio.put({ "_id": "id" + i, "director": "D" + i, "year": i, "title": "T" + i }); o.tmp = o.fakeIndexA.free.pop() || o.fakeIndexA.database.length; o.fakeIndexA.database[o.tmp] = {"_id": "id" + i, "director": "D" + i}; o.tmp = o.fakeIndexB.free.pop() || o.fakeIndexB.database.length; o.fakeIndexB.database[o.tmp] = {"_id": "id" + i, "year": i}; } o.clock.tick(5000); o.spy(o, "status", 40, "Check database"); o.jio.check({"_id": "A"}, o.f); o.tick(o); o.spy(o, "status", 40, "Check database"); o.jio.check({"_id": "B"}, o.f); o.tick(o); o.spy(o, "value", {"_id": "A", "ok": true}, "Repair database"); o.jio.repair({"_id": "A"}, o.f); o.tick(o); o.spy(o, "value", {"_id": "B", "ok": true}, "Repair database"); o.jio.repair({"_id": "B"}, o.f); o.tick(o); o.spy(o, "value", {"_id": "A", "ok": true}, "Check database again"); o.jio.check({"_id": "A"}, o.f); o.tick(o); o.spy(o, "value", {"_id": "B", "ok": true}, "Check database again"); o.jio.check({"_id": "B"}, o.f); o.tick(o); // check index file o.spy(o, "value", o.fakeIndexA, "Manually check index file"); o.jio.get({"_id": "A"}, function (err, response) { if (response) { delete response.location; response.database.sort(function (a, b) { return a._id < b._id ? -1 : a._id > b._id ? 1 : 0; }); } o.f(err, response); }); o.tick(o); o.spy(o, "value", o.fakeIndexB, "Manually check index file"); o.jio.get({"_id": "B"}, function (err, response) { if (response) { delete response.location; response.database.sort(function (a, b) { return a._id < b._id ? -1 : a._id > b._id ? 1 : 0; }); } o.f(err, response); }); o.tick(o); o.jio2 = JIO.newJio({"type": "local", "username": "indexstoragerepair"}); o.jio2.put({"_id": "blah", "title": "t", "year": "y", "director": "d"}); o.clock.tick(1000); o.jio2.stop(); o.fakeIndexA.database.unshift({"_id": "blah", "director": "d"}); o.fakeIndexB.database.unshift({"_id": "blah", "year": "y"}); o.spy(o, "status", 40, "Check Document"); o.jio.check({"_id": "blah"}, o.f) o.tick(o); o.spy(o, "value", {"id": "blah", "ok": true}, "Repair Document"); o.jio.repair({"_id": "blah"}, o.f) o.tick(o); o.spy(o, "value", {"id": "blah", "ok": true}, "Check Document again"); o.jio.repair({"_id": "blah"}, o.f) o.tick(o); // check index file o.spy(o, "value", o.fakeIndexA, "Manually check index file"); o.jio.get({"_id": "A"}, function (err, response) { if (response) { delete response.location; response.database.sort(function (a, b) { return a._id < b._id ? -1 : a._id > b._id ? 1 : 0; }); } o.f(err, response); }); o.tick(o); o.spy(o, "value", o.fakeIndexB, "Manually check index file"); o.jio.get({"_id": "B"}, function (err, response) { if (response) { delete response.location; response.database.sort(function (a, b) { return a._id < b._id ? -1 : a._id > b._id ? 1 : 0; }); } o.f(err, response); }); o.tick(o); o.jio.stop(); }); test ("PutAttachment", function(){ // not sure these need to be run, because the index does not change // and only small modifications have been made to handle putAttachment // tests are from localStorage putAttachment var o = generateTools(this); o.jio = JIO.newJio({ "type": "indexed", "indices": [ {"id": "A", "index": ["author"]}, {"id": "B", "index": ["year"]} ], "sub_storage": { "type": "local", "username": "iputatt", "application_name": "iputatt" } }); // putAttachment without doc id // error 20 -> document id required o.spy(o, "status", 20, "PutAttachment without doc id"); o.jio.putAttachment({}, o.f); o.tick(o); // putAttachment without attachment id // error 22 -> attachment id required o.spy(o, "status", 22, "PutAttachment without attachment id"); o.jio.putAttachment({"_id": "putattmt1"}, o.f); o.tick(o); // putAttachment without document // error 404 -> not found o.spy(o, "status", 404, "PutAttachment without document"); o.jio.putAttachment({"_id": "putattmt1", "_attachment": "putattmt2"}, o.f); o.tick(o); // putAttachment with document o.doc = {"_id": "putattmt1","title": "myPutAttmt1"}; o.spy (o, "value", {"ok": true, "id": "putattmt1"}, "Put underlying document"); o.jio.put(o.doc, o.f); o.tick(o); o.spy(o, "value", {"ok": true, "id": "putattmt1", "attachment": "putattmt2"}, "PutAttachment with document, without data"); o.jio.putAttachment({"_id": "putattmt1", "_attachment": "putattmt2"}, o.f); o.tick(o); // check document deepEqual( localstorage.getItem("jio/localstorage/iputatt/iputatt/putattmt1"), { "_id": "putattmt1", "title": "myPutAttmt1", "_attachments": { "putattmt2": { "length": 0, // md5("") "digest": "md5-d41d8cd98f00b204e9800998ecf8427e" } } }, "Check document" ); // check attachment deepEqual( localstorage.getItem( "jio/localstorage/iputatt/iputatt/putattmt1/putattmt2"), "", "Check attachment" ); // update attachment o.spy(o, "value", {"ok": true, "id": "putattmt1", "attachment": "putattmt2"}, "Update Attachment, with data"); o.jio.putAttachment({ "_id": "putattmt1", "_attachment": "putattmt2", "_data": "abc" }, o.f); o.tick(o); // check document deepEqual( localstorage.getItem("jio/localstorage/iputatt/iputatt/putattmt1"), { "_id": "putattmt1", "title": "myPutAttmt1", "_attachments": { "putattmt2": { "length": 3, // md5("abc") "digest": "md5-900150983cd24fb0d6963f7d28e17f72" } } }, "Check document" ); // check attachment deepEqual( localstorage.getItem( "jio/localstorage/iputatt/iputatt/putattmt1/putattmt2"), "abc", "Check attachment" ); o.jio.stop(); }); test ("Get", function(){ // not sure these need to be run, because the index does not change // and only small modifications have been made to handle putAttachment // tests are from localStorage putAttachment var o = generateTools(this); o.jio = JIO.newJio({ "type": "indexed", "indices": [ {"id": "A", "index": ["author"]}, {"id": "B", "index": ["year"]} ], "sub_storage": { "type": "local", "username": "iget", "application_name": "iget" } }); // get inexistent document o.spy(o, "status", 404, "Get inexistent document"); o.jio.get({"_id": "get1"}, o.f); o.tick(o); // get inexistent attachment o.spy(o, "status", 404, "Get inexistent attachment"); o.jio.getAttachment({"_id": "get1", "_attachment": "get2"}, o.f); o.tick(o); // adding a document o.doc_get1 = { "_id": "get1", "title": "myGet1" }; localstorage.setItem("jio/localstorage/iget/iget/get1", o.doc_get1); // get document o.spy(o, "value", o.doc_get1, "Get document"); o.jio.get({"_id": "get1"}, o.f); o.tick(o); // get inexistent attachment (document exists) o.spy(o, "status", 404, "Get inexistent attachment (document exists)"); o.jio.getAttachment({"_id": "get1", "_attachment": "get2"}, o.f); o.tick(o); // adding an attachment o.doc_get1["_attachments"] = { "get2": { "length": 2, // md5("de") "digest": "md5-5f02f0889301fd7be1ac972c11bf3e7d" } }; localstorage.setItem("jio/localstorage/iget/iget/get1", o.doc_get1); localstorage.setItem("jio/localstorage/iget/iget/get1/get2", "de"); // get attachment o.spy(o, "value", "de", "Get attachment"); o.jio.getAttachment({"_id": "get1", "_attachment": "get2"}, o.f); o.tick(o); o.jio.stop(); }); test ("Remove", function(){ // not sure these need to be run, because the index does not change // and only small modifications have been made to handle putAttachment // tests are from localStorage putAttachment var o = generateTools(this); o.jio = JIO.newJio({ "type": "indexed", "indices": [ {"id": "A", "index": ["author"]}, {"id": "B", "index": ["year"]} ], "sub_storage": { "type": "local", "username": "irem", "application_name": "irem" } }); // remove inexistent document o.spy(o, "status", 404, "Remove inexistent document"); o.jio.remove({"_id": "remove1"}, o.f); o.tick(o); // remove inexistent document/attachment o.spy(o, "status", 404, "Remove inexistent attachment"); o.jio.removeAttachment({"_id": "remove1", "_attachment": "remove2"}, o.f); o.tick(o); // adding a document o.jio.put({"_id": "remove1", "title": "myRemove1", "author": "Mr. President", "year": "2525" }); o.tick(o); // adding a 2nd document with same keywords o.jio.put({"_id": "removeAlso", "title": "myRemove2", "author": "Martin Mustermann", "year": "2525" }); o.tick(o); // remove document o.spy(o, "value", {"ok": true, "id": "remove1"}, "Remove document"); o.jio.remove({"_id": "remove1"}, o.f); o.tick(o); // check index o.fakeIndexA = { "_id": "A", "indexing": ["author"], "free": [0], "location": { "removeAlso": 1 }, "database": [null, {"_id": "removeAlso", "author": "Martin Mustermann"}] }; o.spy(o, "value", o.fakeIndexA, "Check index file"); o.jio.get({"_id": "A"}, o.f); o.tick(o); o.fakeIndexB = { "_id": "B", "indexing": ["year"], "free": [0], "location": { "removeAlso": 1 }, "database": [null, {"_id": "removeAlso", "year": "2525"}] }; o.spy(o, "value", o.fakeIndexB, "Check index file"); o.jio.get({"_id": "B"}, o.f); o.tick(o); // check document o.spy(o, "status", 404, "Check if document has been removed"); o.jio.get({"_id": "remove1"}, o.f); o.tick(o); // adding a new document o.jio.put({"_id": "remove3", "title": "myRemove1", "author": "Mrs Sunshine", "year": "1234" }); o.tick(o); // adding an attachment o.jio.putAttachment({ "_id": "remove3", "_attachment": "removeAtt", "_mimetype": "text/plain", "_data": "hello"}); o.tick(o); // add another attachment o.jio.putAttachment({ "_id": "remove3", "_attachment": "removeAtt2", "_mimetype": "text/plain", "_data": "hello2"}); o.tick(o); // remove attachment o.spy(o, "value", {"ok": true, "id": "remove3", "attachment": "removeAtt2"}, "Remove one of multiple attachment"); o.jio.removeAttachment({"_id": "remove3", "_attachment": "removeAtt2"}, o.f); o.tick(o); // check index o.fakeIndexA.free = []; o.fakeIndexA.location.remove3 = 0; o.fakeIndexA.database[0] = {"_id": "remove3", "author": "Mrs Sunshine"}; o.spy(o, "value", o.fakeIndexA, "Check index file"); o.jio.get({"_id": "A"}, o.f); o.tick(o); o.fakeIndexB.free = []; o.fakeIndexB.location.remove3 = 0; o.fakeIndexB.database[0] = {"_id": "remove3", "year": "1234"}; o.spy(o, "value", o.fakeIndexB, "Check index file"); o.jio.get({"_id": "B"}, o.f); o.tick(o); // remove document and attachment together o.spy(o, "value", {"ok": true, "id": "remove3"}, "Remove one document and attachment together"); o.jio.remove({"_id": "remove3"}, o.f); o.tick(o); // check index o.fakeIndexA.free = [0]; delete o.fakeIndexA.location.remove3; o.fakeIndexA.database[0] = null; o.spy(o, "value", o.fakeIndexA, "Check index file"); o.jio.get({"_id": "A"}, o.f); o.tick(o); o.fakeIndexB.free = [0]; delete o.fakeIndexB.location.remove3; o.fakeIndexB.database[0] = null; o.spy(o, "value", o.fakeIndexB, "Check index file"); o.jio.get({"_id": "B"}, o.f); o.tick(o); // check attachment o.spy(o, "status", 404, "Check if attachment has been removed"); o.jio.getAttachment({"_id": "remove3", "_attachment": "removeAtt"}, o.f); o.tick(o); // check document o.spy(o, "status", 404, "Check if document has been removed"); o.jio.get({"_id": "remove3"}, o.f); o.tick(o); o.jio.stop(); }); test ("AllDocs", function () { var o = generateTools(this); o.jio = JIO.newJio({ "type": "indexed", "indices": [ {"id": "A", "index": ["author"]}, {"id": "B", "index": ["year"]} ], "sub_storage": { "type": "local", "username": "iall", "application_name": "iall" } }); // adding documents o.all1 = { "_id": "dragon.doc", "title": "some title", "author": "Dr. No", "year": "1968" }; o.spy (o, "value", {"ok": true, "id": "dragon.doc"}, "Put 1"); o.jio.put(o.all1, o.f); o.tick(o); o.all2 = {"_id": "timemachine", "title": "hello world", "author": "Dr. Who", "year": "1968" } o.spy (o, "value", {"ok": true, "id": "timemachine"}, "Put 2"); o.jio.put(o.all2, o.f); o.tick(o); o.all3 = {"_id": "rocket.ppt", "title": "sunshine.", "author": "Dr. Snuggles", "year": "1985" } o.spy (o, "value", {"ok": true, "id": "rocket.ppt"}, "Put 3"); o.jio.put(o.all3, o.f); o.tick(o); o.all4 = {"_id": "stick.jpg", "title": "clouds", "author": "Dr. House", "year": "2005" } o.spy (o, "value", {"ok": true, "id": "stick.jpg"}, "Put 4"); o.jio.put(o.all4, o.f); o.tick(o); // check index o.fakeIndexA = { "_id": "A", "indexing": ["author"], "free": [], "location": { "dragon.doc": 0, "timemachine": 1, "rocket.ppt": 2, "stick.jpg": 3 }, "database": [ {"_id": "dragon.doc", "author": "Dr. No"}, {"_id": "timemachine", "author": "Dr. Who"}, {"_id": "rocket.ppt", "author": "Dr. Snuggles"}, {"_id": "stick.jpg", "author": "Dr. House"} ] }; o.spy(o, "value", o.fakeIndexA, "Check index file"); o.jio.get({"_id": "A"}, o.f); o.tick(o); o.thisShouldBeTheAnswer = { "rows": [ {"id": "dragon.doc", "key": "dragon.doc", "value": {} }, {"id": "timemachine", "key": "timemachine", "value": {} }, {"id": "rocket.ppt", "key": "rocket.ppt", "value": {} }, {"id": "stick.jpg", "key": "stick.jpg", "value": {} } ], "total_rows": 4 } o.spy(o, "value", o.thisShouldBeTheAnswer, "allDocs (served by index)"); o.jio.allDocs(o.f); o.tick(o); o.jio.stop(); }); test ("AllDocs Complex Queries", function () { var o = generateTools(this), i, m = 15; o.jio = JIO.newJio({ "type": "indexed", "indices": [ {"id":"A", "index": ["director"]}, {"id":"B", "index": ["title", "year"]} ], "sub_storage": { "type": "local", "username": "icomplex", "application_name": "acomplex" } }); o.localpath = "jio/localstorage/icomplex/acomplex"; // sample data o.titles = ["Shawshank Redemption", "Godfather", "Godfather 2", "Pulp Fiction", "The Good, The Bad and The Ugly", "12 Angry Men", "The Dark Knight", "Schindlers List", "Lord of the Rings - Return of the King", "Fight Club", "Star Wars Episode V", "Lord Of the Rings - Fellowship of the Ring", "One flew over the Cuckoo's Nest", "Inception", "Godfellas" ]; o.years = [1994,1972,1974,1994,1966,1957,2008,1993,2003,1999,1980,2001, 1975,2010,1990]; o.director = ["Frank Darabont", "Francis Ford Coppola", "Francis Ford Coppola", "Quentin Tarantino", "Sergio Leone", "Sidney Lumet", "Christopher Nolan", "Steven Spielberg", "Peter Jackson", "David Fincher", "Irvin Kershner", "Peter Jackson", "Milos Forman", "Christopher Nolan", " Martin Scorsese" ]; o.fakeIndexA = { "_id": "A", "indexing": ["director"], "free": [], "location": {}, "database": [] }; o.fakeIndexB = { "_id": "B", "indexing": ["title", "year"], "free": [], "location": {}, "database": [] }; for (i = 0; i < m; i += 1) { o.jio.put({ "_id": "" + i, "director": o.director[i], "year": o.years[i], "title": o.titles[i] }); o.tmp = o.fakeIndexA.free.pop() || o.fakeIndexA.database.length; o.fakeIndexA.database[o.tmp] = {"_id": "" + i, "director": o.director[i]}; o.fakeIndexA.location["" + i] = o.tmp; o.tmp = o.fakeIndexB.free.pop() || o.fakeIndexB.database.length; o.fakeIndexB.database[o.tmp] = { "_id": "" + i, "year": o.years[i], "title": o.titles[i] }; o.fakeIndexB.location["" + i] = o.tmp; o.clock.tick(1000); } // o.clock.tick(1000); // check index file o.spy(o, "value", o.fakeIndexA, "Check index file"); o.jio.get({"_id": "A"}, o.f); o.tick(o); o.spy(o, "value", o.fakeIndexB, "Check index file"); o.jio.get({"_id": "B"}, o.f); o.tick(o); // response o.allDocsResponse = {}; o.allDocsResponse.rows = []; o.allDocsResponse.total_rows = m; for (i = 0; i < m; i += 1) { o.allDocsResponse.rows.push({ "id": ""+i, "key": ""+i, "value": {}, "doc": { "_id": ""+i, "title": o.titles[i], "year": o.years[i], "director": o.director[i] } }); } o.response = JSON.parse(JSON.stringify(o.allDocsResponse)); for (i = 0; i < o.response.rows.length; i += 1) { delete o.response.rows[i].doc; } // alldocs o.spy(o, "value", o.response, "AllDocs response generated from index"); o.jio.allDocs(o.f); o.tick(o, 1000); // complex queries o.response = JSON.parse(JSON.stringify(o.allDocsResponse)); i = 0; while (i < o.response.rows.length) { if (o.response.rows[i].year < 1980) { o.response.rows.splice(i, 1); } else { o.response.rows[i].value = { "year": o.response.rows[i].doc.year, "title": o.response.rows[i].doc.title } delete o.response.rows[i].doc; i += 1; } } o.response.rows.sort(function (a, b) { return a.value.year > b.value.year ? -1 : a.value.year < b.value.year ? 1 : 0; }); o.response.rows.length = 5; o.response.total_rows = 5; o.spy(o, "value", o.response, "allDocs (complex queries year >= 1980, index used to do query)"); o.jio.allDocs({ // "query":'(year: >= "1980" AND year: < "2000")', "query": '(year: >= "1980")', "limit": [0, 5], "sort_on": [['year', 'descending']], "select_list": ['title', 'year'] }, o.f); o.tick(o); // complex queries o.spy(o, "value", {"total_rows": 0, "rows": []}, "allDocs (complex queries year >= 1980, can't use index)"); o.jio.allDocs({ // "query":'(year: >= "1980" AND year: < "2000")', "query": '(year: >= "1980")', "limit": [0, 5], "sort_on": [['year','descending']], "select_list": ['director', 'year'] }, o.f); o.tick(o); // empty query returns all o.response = JSON.parse(JSON.stringify(o.allDocsResponse)); i = 0; while (i < o.response.rows.length) { o.response.rows[i].value.title = o.response.rows[i].doc.title; delete o.response.rows[i].doc; i += 1; } o.response.rows.sort(function (a, b) { return a.value.title > b.value.title ? -1 : a.value.title < b.value.title ? 1 : 0; }); o.spy(o, "value", o.response, "allDocs (empty query in complex query)"); o.jio.allDocs({ "sort_on":[['title','descending']], "select_list":['title'] }, o.f); o.tick(o); o.jio.stop(); }); /* module ('Jio CryptedStorage'); test ('Document save' , function () { var o = {}, clock = this.sandbox.useFakeTimers(); clock.tick(base_tick); o.jio=JIO.newJio({type:'crypt', username:'cryptsave', password:'mypwd', storage:{type:'local', username:'cryptsavelocal', application_name:'jiotests'}}); o.f = function (err,val) { if (err) { err = err.status; } deepEqual (err || val,{ok:true,id:'testsave'},'save ok'); }; this.spy(o,'f'); o.jio.put({_id:'testsave',content:'contentoftest'},o.f); clock.tick(1000); if (!o.f.calledOnce) { ok (false, 'no response / too much results'); } // encrypt 'testsave' with 'cryptsave:mypwd' password o.tmp = LocalOrCookieStorage.getItem( // '/' = '%2F' 'jio/local/cryptsavelocal/jiotests/rZx5PJxttlf9QpZER%2F5x354bfX54QFa1'); if (o.tmp) { delete o.tmp._last_modified; delete o.tmp._creation_date; } deepEqual (o.tmp, {_id:'rZx5PJxttlf9QpZER/5x354bfX54QFa1', content:'upZkPIpitF3QMT/DU5jM3gP0SEbwo1n81rMOfLE'}, 'Check if the document is realy encrypted'); o.jio.stop(); }); test ('Document load' , function () { var o = {}, clock = this.sandbox.useFakeTimers(); clock.tick(base_tick); o.jio=JIO.newJio({type:'crypt', username:'cryptload', password:'mypwd', storage:{type:'local', username:'cryptloadlocal', application_name:'jiotests'}}); o.f = function (err,val) { deepEqual (err || val,{ _id:'testload',content:'contentoftest', _last_modified:500,_creation_date:500},'load ok'); }; this.spy(o,'f'); // encrypt 'testload' with 'cryptload:mypwd' password // and 'contentoftest' with 'cryptload:mypwd' o.doc = { _id:'hiG4H80pwkXCCrlLl1X0BD0BfWLZwDUX', content:'kSulH8Qo105dSKHcY2hEBXWXC9b+3PCEFSm1k7k', _last_modified:500,_creation_date:500}; addFileToLocalStorage('cryptloadlocal','jiotests',o.doc); o.jio.get('testload',o.f); clock.tick(1000); if (!o.f.calledOnce) { ok (false, 'no response / too much results'); } o.jio.stop(); }); test ('Get Document List', function () { var o = {}, clock = this.sandbox.useFakeTimers(); clock.tick(base_tick); o.jio=JIO.newJio({type:'crypt', username:'cryptgetlist', password:'mypwd', storage:{type:'local', username:'cryptgetlistlocal', application_name:'jiotests'}}); o.f = function (err,val) { deepEqual (err || objectifyDocumentArray(val.rows), objectifyDocumentArray(o.doc_list),'Getting list'); }; o.tick = function (tick) { clock.tick (tick || 1000); if (!o.f.calledOnce) { if (o.f.called) { ok (false, 'too much results'); } else { ok (false, 'no response'); } } }; this.spy(o,'f'); o.doc_list = [{ id:'testgetlist1',key:'testgetlist1',value:{ _last_modified:500,_creation_date:200} },{ id:'testgetlist2',key:'testgetlist2',value:{ _last_modified:300,_creation_date:300} }]; o.doc_encrypt_list = [ {_id:'541eX0WTMDw7rqIP7Ofxd1nXlPOtejxGnwOzMw', content:'/4dBPUdmLolLfUaDxPPrhjRPdA', _last_modified:500,_creation_date:200}, {_id:'541eX0WTMDw7rqIMyJ5tx4YHWSyxJ5UjYvmtqw', content:'/4FBALhweuyjxxD53eFQDSm4VA', _last_modified:300,_creation_date:300} ]; // encrypt with 'cryptgetlist:mypwd' as password LocalOrCookieStorage.setItem( 'jio/local_file_name_array/cryptgetlistlocal/jiotests', [o.doc_encrypt_list[0]._id,o.doc_encrypt_list[1]._id]); LocalOrCookieStorage.setItem( 'jio/local/cryptgetlistlocal/jiotests/'+o.doc_encrypt_list[0]._id, o.doc_encrypt_list[0]); LocalOrCookieStorage.setItem( 'jio/local/cryptgetlistlocal/jiotests/'+o.doc_encrypt_list[1]._id, o.doc_encrypt_list[1]); o.jio.allDocs(o.f); o.tick(10000); o.jio.stop(); }); test ('Remove document', function () { var o = {}, clock = this.sandbox.useFakeTimers(); clock.tick(base_tick); o.jio=JIO.newJio({type:'crypt', username:'cryptremove', password:'mypwd', storage:{type:'local', username:'cryptremovelocal', application_name:'jiotests'}}); o.f = function (err,val) { deepEqual (err || val,{ok:true,id:'file'},'Document remove'); }; this.spy(o,'f'); // encrypt with 'cryptremove:mypwd' as password o.doc = {_id:'JqCLTjyxQqO9jwfxD/lyfGIX+qA', content:'LKaLZopWgML6IxERqoJ2mUyyO', _last_modified:500,_creation_date:500}; o.jio.remove({_id:'file'},o.f); clock.tick(1000); if (!o.f.calledOnce){ ok (false, 'no response / too much results'); } o.jio.stop(); }); module ('Jio ConflictManagerStorage'); test ('Simple methods', function () { // Try all the simple methods like saving, loading, removing a document and // getting a list of document without testing conflicts var o = {}; o.clock = this.sandbox.useFakeTimers(); o.t = this; o.clock.tick(base_tick); o.spy = function(value,message) { o.f = function(err,val) { deepEqual (err || val,value,message); }; o.t.spy(o,'f'); }; o.tick = function (tick) { o.clock.tick(tick || 1000); if (!o.f.calledOnce) { if (o.f.called) { ok(false, 'too much results'); } else { ok(false, 'no response'); } } }; o.jio = JIO.newJio({type:'conflictmanager', username:'methods', storage:{type:'local', username:'conflictmethods', application_name:'jiotests'}}); // PUT o.spy({ok:true,id:'file.doc',rev:'1'},'saving "file.doc".'); o.jio.put({_id:'file.doc',content:'content1'},function (err,val) { if (val) { o.rev1 = val.rev; val.rev = val.rev.split('-')[0]; } o.f (err,val); }); o.tick(); // PUT with options o.spy({ok:true,id:'file2.doc',rev:'1', conflicts:{total_rows:0,rows:[]}, revisions:{start:1,ids:['1']}, revs_info:[{rev:'1',status:'available'}]}, 'saving "file2.doc".'); o.jio.put({_id:'file2.doc',content:'yes'}, {revs:true,revs_info:true,conflicts:true}, function (err,val) { if (val) { o.rev2 = val.rev; val.rev = val.rev.split('-')[0]; if (val.revs_info) { if (val.revisions) { makeRevsAccordingToRevsInfo( val.revisions,val.revs_info); } val.revs_info[0].rev = val.revs_info[0].rev.split('-')[0]; } } o.f (err,val); }); o.tick(); // GET o.get_callback = function (err,val) { if (val) { val._rev = (val._rev?val._rev.split('-')[0]:'/'); val._creation_date = (val._creation_date?true:undefined); val._last_modified = (val._last_modified?true:undefined); } o.f(err,val); }; o.spy({_id:'file.doc',content:'content1',_rev:'1', _creation_date:true,_last_modified:true},'loading "file.doc".'); o.jio.get('file.doc',o.get_callback); o.tick(); // GET with options o.get_callback = function (err,val) { if (val) { val._rev = (val._rev?val._rev.split('-')[0]:'/'); val._creation_date = (val._creation_date?true:undefined); val._last_modified = (val._last_modified?true:undefined); if (val._revs_info) { if (val._revisions) { makeRevsAccordingToRevsInfo( val._revisions,val._revs_info); } val._revs_info[0].rev = val._revs_info[0].rev.split('-')[0]; } } o.f(err,val); }; o.spy({_id:'file2.doc',content:'yes',_rev:'1', _creation_date:true,_last_modified:true, _conflicts:{total_rows:0,rows:[]}, _revisions:{start:1,ids:['1']}, _revs_info:[{rev:'1',status:'available'}]}, 'loading "file2.doc".'); o.jio.get('file2.doc',{revs:true,revs_info:true,conflicts:true}, o.get_callback); o.tick(); // allDocs o.spy({total_rows:2,rows:[{ id:'file.doc',key:'file.doc', value:{_rev:'1',_creation_date:true,_last_modified:true} },{ id:'file2.doc',key:'file2.doc', value:{_rev:'1',_creation_date:true,_last_modified:true} }]},'getting list.'); o.jio.allDocs(function (err,val) { if (val) { var i; for (i = 0; i < val.total_rows; i+= 1) { val.rows[i].value._creation_date = val.rows[i].value._creation_date? true:undefined; val.rows[i].value._last_modified = val.rows[i].value._last_modified? true:undefined; val.rows[i].value._rev = val.rows[i].value._rev.split('-')[0]; } // because the result can be disordered if (val.total_rows === 2 && val.rows[0].id === 'file2.doc') { var tmp = val.rows[0]; val.rows[0] = val.rows[1]; val.rows[1] = tmp; } } o.f(err,val); }); o.tick(); // remove o.spy({ok:true,id:'file.doc',rev:'2'}, 'removing "file.doc"'); o.jio.remove({_id:'file.doc'},{rev:o.rev1},function (err,val) { if (val) { val.rev = val.rev?val.rev.split('-')[0]:undefined; } o.f(err,val); }); o.tick(); // remove with options o.spy({ ok:true,id:'file2.doc',rev:'2', conflicts:{total_rows:0,rows:[]}, revisions:{start:2,ids:['2',getHashFromRev(o.rev2)]}, revs_info:[{rev:'2',status:'deleted'}] },'removing "file2.doc"'); o.jio.remove( {_id:'file2.doc'}, {rev:o.rev2,conflicts:true,revs:true,revs_info:true}, function (err,val) { if (val) { val.rev = val.rev?val.rev.split('-')[0]:undefined; if (val.revs_info) { if (val.revisions) { makeRevsAccordingToRevsInfo( val.revisions,val.revs_info); } val.revs_info[0].rev = val.revs_info[0].rev.split('-')[0]; } } o.f(err,val); }); o.tick(); o.spy(404,'loading document fail.'); o.jio.get('file.doc',function (err,val) { if (err) { err = err.status; } o.f(err,val); }); o.tick(); o.jio.stop(); }); test ('Revision Conflict', function() { // Try to tests all revision conflict possibility var o = {}; o.clock = this.sandbox.useFakeTimers(); o.t = this; o.clock.tick (base_tick); o.spy = basic_spy_function; o.tick = basic_tick_function; o.localNamespace = 'jio/local/revisionconflict/jiotests/'; o.rev={}; o.checkContent = function (string,message) { ok (LocalOrCookieStorage.getItem(o.localNamespace + string), message || '"' + string + '" is saved.'); }; o.checkNoContent = function (string,message) { ok (!LocalOrCookieStorage.getItem(o.localNamespace + string), message || '"' + string + '" does not exists.'); }; o.sub_storage_spec = {type:'local', username:'revisionconflict', application_name:'jiotests'} ////////////////////////////////////////////////////////////////////// o.jio = JIO.newJio({type:'conflictmanager', storage:o.sub_storage_spec}); // create a new file o.spy(o,'value', {ok:true,id:'file.doc',rev:'1',conflicts:{total_rows:0,rows:[]}, revs_info:[{rev:'1',status:'available'}], revisions:{start:1,ids:['1']}}, 'new file "file.doc".'); o.jio.put( {_id:'file.doc',content:'content1'}, {revs:true,revs_info:true,conflicts:true}, function (err,val) { if (val) { o.rev.first = val.rev; val.rev = val.rev?val.rev.split('-')[0]:undefined; if (val.revs_info) { if (val.revisions) { makeRevsAccordingToRevsInfo( val.revisions,val.revs_info); } val.revs_info[0].rev = val.revs_info[0].rev.split('-')[0]; } } o.f(err,val); } ); o.tick(o); o.checkContent('file.doc.'+o.rev.first); // modify the file o.spy(o,'value', {ok:true,id:'file.doc',rev:'2', conflicts:{total_rows:0,rows:[]}, revisions:{start:2,ids:['2',getHashFromRev(o.rev.first)]}, revs_info:[{rev:'2',status:'available'}]}, 'modify "file.doc", revision: "'+ o.rev.first+'".'); o.jio.put( {_id:'file.doc',content:'content2',_rev:o.rev.first}, {revs:true,revs_info:true,conflicts:true}, function (err,val) { if (val) { o.rev.second = val.rev; val.rev = val.rev?val.rev.split('-')[0]:undefined; if (val.revs_info) { if (val.revisions) { makeRevsAccordingToRevsInfo( val.revisions,val.revs_info); } val.revs_info[0].rev = val.revs_info[0].rev.split('-')[0]; } } o.f(err,val); } ); o.tick(o); o.checkContent('file.doc.'+o.rev.second); o.checkNoContent('file.doc.'+o.rev.first); // modify the file from the second revision instead of the third o.test_message = 'modify "file.doc", revision: "'+ o.rev.first+'" -> conflict!'; o.f = o.t.spy(); o.jio.put( {_id:'file.doc',content:'content3',_rev:o.rev.first}, {revs:true,revs_info:true,conflicts:true},function (err,val) { o.f(); var k; if (err) { o.rev.third = err.rev; err.rev = checkRev(err.rev); if (err.conflicts && err.conflicts.rows) { o.tmp = err.conflicts; o.solveConflict = checkConflictRow (err.conflicts.rows[0]); } for (k in {'error':0,'message':0,'reason':0,'statusText':0}) { if (err[k]) { delete err[k]; } else { err[k] = 'ERROR: ' + k + ' is missing !'; } } } deepEqual(err||val,{ rev:o.rev.third, conflicts:{total_rows:1,rows:[ {id:'file.doc',key:[o.rev.second,o.rev.third], value:{_solveConflict:'function'}}]}, status:409, // just one revision in the history, it does not keep older // revisions because it is not a revision manager storage. revisions:{start:1,ids:[getHashFromRev(o.rev.third)]}, revs_info:[{rev:o.rev.second,status:'available'}, {rev:o.rev.third,status:'available'}] },o.test_message); ok (!revs_infoContains(err.revs_info,o.rev.first), 'check if the first revision is not include to '+ 'the conflict list.'); ok (revs_infoContains(err.revs_info,err.rev), 'check if the new revision is include to '+ 'the conflict list.'); }); o.tick(o); o.checkContent ('file.doc.'+o.rev.third); // loading test o.spy(o,'value',{_id:'file.doc',_rev:o.rev.third,content:'content3', _conflicts:o.tmp}, 'loading "file.doc" -> conflict!'); o.jio.get('file.doc',{conflicts:true},function (err,val) { var k; if (val) { if (val._conflicts && val._conflicts.rows) { checkConflictRow (val._conflicts.rows[0]); } for (k in {'_creation_date':0,'_last_modified':0}) { if (val[k]) { delete val[k]; } else { val[k] = 'ERROR: ' + k + ' is missing !'; } } } o.f(err,val); }); o.tick(o); if (!o.solveConflict) { return ok(false,'Cannot to continue the tests'); } // solving conflict o.spy(o,'value',{ok:true,id:'file.doc',rev:'3'}, 'solve conflict "file.doc".'); o.solveConflict( 'content4',function (err,val) { if (val) { o.rev.forth = val.rev; val.rev = val.rev?val.rev.split('-')[0]:undefined; } o.f(err,val); }); o.tick(o); o.checkContent('file.doc.'+o.rev.forth); o.checkNoContent('file.doc.'+o.rev.second); o.checkNoContent('file.doc.'+o.rev.third); o.jio.stop(); }); test ('Conflict in a conflict solving', function () { var o = {}; o.clock = this.sandbox.useFakeTimers(); o.t = this; o.clock.tick (base_tick); o.spy = basic_spy_function; o.tick = basic_tick_function; o.localNamespace = 'jio/local/conflictconflict/jiotests/'; o.rev={}; o.checkContent = function (string,message) { ok (LocalOrCookieStorage.getItem(o.localNamespace + string), message || '"' + string + '" is saved.'); }; o.checkNoContent = function (string,message) { ok (!LocalOrCookieStorage.getItem(o.localNamespace + string), message || '"' + string + '" does not exists.'); }; o.sub_storage_spec = {type:'local', username:'conflictconflict', application_name:'jiotests'} ////////////////////////////////////////////////////////////////////// o.jio = JIO.newJio({type:'conflictmanager', storage:o.sub_storage_spec}); // create a new file o.test_message = 'new file "file.doc", revision: "0".' o.f = o.t.spy(); o.jio.put( {_id:'file.doc',content:'content1'}, {conflicts:true,revs:true,revs_info:true}, function(err,val) { o.f(); if (val) { o.rev.first = val.rev; val.rev = checkRev(val.rev); } deepEqual(err||val,{ ok:true,id:'file.doc',rev:o.rev.first, conflicts:{total_rows:0,rows:[]}, revisions:{start:1,ids:[getHashFromRev(o.rev.first)]}, revs_info:[{rev:o.rev.first,status:'available'}] },o.test_message); }); o.tick(o); o.checkContent ('file.doc.'+o.rev.first); // modify the file from the second revision instead of the third o.test_message = 'modify "file.doc", revision: "0" -> conflict!'; o.f = o.t.spy(); o.jio.put( {_id:'file.doc',content:'content2'}, {conflicts:true,revs:true,revs_info:true}, function (err,val) { o.f(); var k; if (err) { o.rev.second = err.rev; err.rev = checkRev(err.rev); if (err.conflicts && err.conflicts.rows) { o.solveConflict = checkConflictRow (err.conflicts.rows[0]); } for (k in {'error':0,'message':0,'reason':0,'statusText':0}) { if (err[k]) { delete err[k]; } else { err[k] = 'ERROR: ' + k + ' is missing !'; } } } deepEqual(err||val,{ rev:o.rev.second, conflicts:{total_rows:1,rows:[ {id:'file.doc',key:[o.rev.first,o.rev.second], value:{_solveConflict:'function'}}]}, status:409, // just one revision in the history, it does not keep older // revisions because it is not a revision manager storage. revisions:{start:1,ids:[getHashFromRev(o.rev.second)]}, revs_info:[{rev:o.rev.first,status:'available'}, {rev:o.rev.second,status:'available'}] },o.test_message); }); o.tick(o); o.checkContent ('file.doc.'+o.rev.second); if (!o.solveConflict) { return ok(false,'Cannot to continue the tests'); } // saving another time o.test_message = 'modify "file.doc" when solving, revision: "'+ o.rev.first+'" -> conflict!'; o.f = o.t.spy(); o.jio.put( {_id:'file.doc',content:'content3',_rev:o.rev.first}, {conflicts:true,revs:true,revs_info:true}, function(err,val){ o.f(); if (err) { o.rev.third = err.rev; err.rev = checkRev(err.rev); if (err.conflicts && err.conflicts.rows) { checkConflictRow (err.conflicts.rows[0]); } for (k in {'error':0,'message':0,'reason':0,'statusText':0}) { if (err[k]) { delete err[k]; } else { err[k] = 'ERROR: ' + k + ' is missing !'; } } } deepEqual(err||val,{ rev:o.rev.third, conflicts:{total_rows:1,rows:[ {id:'file.doc',key:[o.rev.second,o.rev.third], value:{_solveConflict:'function'}}]}, status:409, // just one revision in the history, it does not keep older // revisions because it is not a revision manager storage. revisions:{start:2,ids:[getHashFromRev(o.rev.third), getHashFromRev(o.rev.first)]}, revs_info:[{rev:o.rev.second,status:'available'}, {rev:o.rev.third,status:'available'}] },o.test_message); }); o.tick(o); o.checkContent ('file.doc.'+o.rev.third); o.checkNoContent ('file.doc.'+o.rev.first); // solving first conflict o.test_message = 'solving conflict "file.doc" -> conflict!'; o.f = o.t.spy(); o.solveConflict( 'content4',{conflicts:true,revs:true,revs_info:true}, function (err,val) { o.f(); if (err) { o.rev.forth = err.rev; err.rev = checkRev(err.rev); if (err.conflicts && err.conflicts.rows) { o.solveConflict = checkConflictRow (err.conflicts.rows[0]); } for (k in {'error':0,'message':0,'reason':0,'statusText':0}) { if (err[k]) { delete err[k]; } else { err[k] = 'ERROR: ' + k + ' is missing !'; } } } deepEqual(err||val,{ rev:o.rev.forth, conflicts:{total_rows:1,rows:[ {id:'file.doc',key:[o.rev.third,o.rev.forth], value:{_solveConflict:'function'}}]}, status:409, // just one revision in the history, it does not keep older // revisions because it is not a revision manager storage. revisions:{start:2,ids:[getHashFromRev(o.rev.forth), getHashFromRev(o.rev.second)]}, revs_info:[{rev:o.rev.third,status:'available'}, {rev:o.rev.forth,status:'available'}] },o.test_message); }); o.tick(o); o.checkContent ('file.doc.'+o.rev.forth); o.checkNoContent ('file.doc.'+o.rev.second); if (!o.solveConflict) { return ok(false,'Cannot to continue the tests'); } // solving last conflict o.test_message = 'solving last conflict "file.doc".'; o.f = o.t.spy(); o.solveConflict( 'content5',{conflicts:true,revs:true,revs_info:true}, function (err,val) { if (val) { o.rev.fifth = val.rev; val.rev = checkRev(val.rev); } deepEqual(err||val,{ ok:true,id:'file.doc',rev:o.rev.fifth, conflicts:{total_rows:0,rows:[]}, revisions:{start:3,ids:[getHashFromRev(o.rev.fifth), getHashFromRev(o.rev.forth), getHashFromRev(o.rev.second)]}, revs_info:[{rev:o.rev.fifth,status:'available'}] },o.test_message); o.f(); }); o.tick(o); o.checkContent ('file.doc.'+o.rev.fifth); o.jio.stop(); }); test ('Remove revision conflict', function () { var o = {}; o.clock = this.sandbox.useFakeTimers(); o.t = this; o.clock.tick (base_tick); o.spy = basic_spy_function; o.tick = basic_tick_function; o.localNamespace = 'jio/local/removeconflict/jiotests/'; o.rev={}; o.checkContent = function (string,message) { ok (LocalOrCookieStorage.getItem(o.localNamespace + string), message || '"' + string + '" is saved.'); }; o.checkNoContent = function (string,message) { ok (!LocalOrCookieStorage.getItem(o.localNamespace + string), message || '"' + string + '" does not exists.'); }; o.sub_storage_spec = {type:'local', username:'removeconflict', application_name:'jiotests'} ////////////////////////////////////////////////////////////////////// o.jio = JIO.newJio({type:'conflictmanager', storage:o.sub_storage_spec}); o.test_message = 'new file "file.doc", revision: "0".'; o.f = o.t.spy(); o.jio.put( {_id:'file.doc',content:'content1'}, {conflicts:true,revs:true,revs_info:true}, function(err,val) { o.f(); if (val) { o.rev.first = val.rev; val.rev = checkRev(val.rev); } deepEqual(err||val,{ ok:true,id:'file.doc',rev:o.rev.first, conflicts:{total_rows:0,rows:[]}, revisions:{start:1,ids:[getHashFromRev(o.rev.first)]}, revs_info:[{rev:o.rev.first,status:'available'}] },o.test_message); }); o.tick(o); o.checkContent ('file.doc.'+o.rev.first); o.test_message = 'remove "file.doc", revision: "wrong" -> conflict!'; o.f = o.t.spy(); o.jio.remove( {_id:'file.doc'}, {conflicts:true,revs:true,revs_info:true,rev:'wrong'}, function (err,val) { o.f(); if (err) { o.rev.second = err.rev; err.rev = checkRev(err.rev); if (err.conflicts && err.conflicts.rows) { o.solveConflict = checkConflictRow (err.conflicts.rows[0]); } for (k in {'error':0,'message':0,'reason':0,'statusText':0}) { if (err[k]) { delete err[k]; } else { err[k] = 'ERROR: ' + k + ' is missing !'; } } } deepEqual(err||val,{ rev:o.rev.second, conflicts:{total_rows:1,rows:[ {id:'file.doc',key:[o.rev.first,o.rev.second], value:{_solveConflict:'function'}}]}, status:409, // just one revision in the history, it does not keep older // revisions because it is not a revision manager storage. revisions:{start:1,ids:[getHashFromRev(o.rev.second)]}, revs_info:[{rev:o.rev.first,status:'available'}, {rev:o.rev.second,status:'deleted'}] },o.test_message); }); o.tick(o); o.test_message = 'new file again "file.doc".'; o.f = o.t.spy(); o.jio.put( {_id:'file.doc',content:'content2'}, {conflicts:true,revs:true,revs_info:true}, function (err,val) { o.f(); if (err) { o.rev.third = err.rev; err.rev = checkRev(err.rev); if (err.conflicts && err.conflicts.rows) { o.solveConflict = checkConflictRow (err.conflicts.rows[0]); } for (k in {'error':0,'message':0,'reason':0,'statusText':0}) { if (err[k]) { delete err[k]; } else { err[k] = 'ERROR: ' + k + ' is missing !'; } } } deepEqual(err||val,{ rev:o.rev.third, conflicts:{total_rows:1,rows:[ {id:'file.doc',key:[o.rev.first,o.rev.second,o.rev.third], value:{_solveConflict:'function'}}]}, status:409, // just one revision in the history, it does not keep older // revisions because it is not a revision manager storage. revisions:{start:1,ids:[getHashFromRev(o.rev.third)]}, revs_info:[{rev:o.rev.first,status:'available'}, {rev:o.rev.second,status:'deleted'}, {rev:o.rev.third,status:'available'}] },o.test_message); }); o.tick(o); o.checkContent ('file.doc.'+o.rev.third); o.test_message = 'remove "file.doc", revision: "'+o.rev.first+ '" -> conflict!' o.f = o.t.spy(); o.jio.remove( {_id:'file.doc'}, {conflicts:true,revs:true,revs_info:true,rev:o.rev.first}, function (err,val) { o.f(); if (err) { o.rev.forth = err.rev; err.rev = checkRev(err.rev); if (err.conflicts && err.conflicts.rows) { o.solveConflict = checkConflictRow (err.conflicts.rows[0]); } for (k in {'error':0,'message':0,'reason':0,'statusText':0}) { if (err[k]) { delete err[k]; } else { err[k] = 'ERROR: ' + k + ' is missing !'; } } } deepEqual(err||val,{ rev:o.rev.forth, conflicts:{total_rows:1,rows:[ {id:'file.doc',key:[o.rev.second,o.rev.third,o.rev.forth], value:{_solveConflict:'function'}}]}, status:409, // just one revision in the history, it does not keep older // revisions because it is not a revision manager storage. revisions:{start:2,ids:[getHashFromRev(o.rev.forth), getHashFromRev(o.rev.first)]}, revs_info:[{rev:o.rev.second,status:'deleted'}, {rev:o.rev.third,status:'available'}, {rev:o.rev.forth,status:'deleted'}] },o.test_message); }); o.tick(o); o.checkNoContent ('file.doc.'+o.rev.first); o.checkNoContent ('file.doc.'+o.rev.forth); if (!o.solveConflict) { return ok(false, 'Cannot continue the tests'); } o.test_message = 'solve "file.doc"'; o.f = o.t.spy(); o.solveConflict({conflicts:true,revs:true,revs_info:true},function(err,val){ o.f(); if (val) { o.rev.fifth = val.rev; val.rev = checkRev(val.rev); } deepEqual(err||val,{ ok:true,id:'file.doc',rev:o.rev.fifth, conflicts:{total_rows:0,rows:[]}, revisions:{start:3,ids:[getHashFromRev(o.rev.fifth), getHashFromRev(o.rev.forth), getHashFromRev(o.rev.first)]}, revs_info:[{rev:o.rev.fifth,status:'deleted'}] },o.test_message); }); o.tick(o); o.checkNoContent ('file.doc.'+o.rev.second); o.checkNoContent ('file.doc.'+o.rev.forth); o.checkNoContent ('file.doc.'+o.rev.fifth); o.test_message = 'save "file3.doc"'; o.f = o.t.spy(); o.jio.put( {_id:'file3.doc',content:'content3'}, function(err,val) { o.f(); if (val) { o.rev.sixth = val.rev; val.rev = checkRev(val.rev); } deepEqual(err||val,{ ok:true,id:'file3.doc',rev:o.rev.sixth },o.test_message); }); o.tick(o); o.test_message = 'save "file3.doc", rev "'+o.rev.sixth+'"'; o.f = o.t.spy(); o.jio.put( {_id:'file3.doc',content:'content3',_rev:o.rev.sixth}, function(err,val) { o.f(); if (val) { o.rev.seventh = val.rev; val.rev = checkRev(val.rev); } deepEqual(err||val,{ ok:true,id:'file3.doc',rev:o.rev.seventh },o.test_message); }); o.tick(o); o.test_message = 'remove last "file3.doc"'; o.f = o.t.spy(); o.jio.remove( {_id:'file3.doc'}, {conflicts:true,revs:true,revs_info:true,rev:'last'}, function (err,val) { o.f(); if (val) { o.rev.eighth = val.rev; val.rev = checkRev(val.rev); } deepEqual(err||val,{ ok:true,id:'file3.doc', rev:o.rev.eighth, conflicts:{total_rows:0,rows:[]}, // just one revision in the history, it does not keep older // revisions because it is not a revision manager storage. revisions:{start:3,ids:[getHashFromRev(o.rev.eighth), getHashFromRev(o.rev.seventh), getHashFromRev(o.rev.sixth)]}, revs_info:[{rev:o.rev.eighth,status:'deleted'}] },o.test_message); }); o.tick(o); o.jio.stop(); }); test ('Load Revisions', function () { var o = {}; o.clock = this.sandbox.useFakeTimers(); o.t = this; o.clock.tick (base_tick); o.spy = basic_spy_function; o.tick = basic_tick_function; o.sub_storage_spec = {type:'local', username:'loadrevisions', application_name:'jiotests'} ////////////////////////////////////////////////////////////////////// o.jio = JIO.newJio({type:'conflictmanager', storage:o.sub_storage_spec}); o.spy(o,'status',404,'load file rev:1,','f'); // 12 === Replaced o.spy(o,'status',404,'load file rev:2','g'); o.spy(o,'status',404,'and load file rev:3 at the same time','h'); o.jio.get('file',{rev:'1'},o.f); o.jio.get('file',{rev:'2'},o.g); o.jio.get('file',{rev:'3'},o.h); o.tick(o,1000,'f'); o.tick(o,0,'g'); o.tick(o,0,'h'); o.jio.stop(); }); test ('Get revision List', function () { var o = {}; o.clock = this.sandbox.useFakeTimers(); o.t = this; o.clock.tick (base_tick); o.spy = basic_spy_function; o.tick = basic_tick_function; o.sub_storage_spec = {type:'local', username:'getrevisionlist', application_name:'jiotests'} o.rev = {}; ////////////////////////////////////////////////////////////////////// o.jio = JIO.newJio({type:'conflictmanager', storage:o.sub_storage_spec}); o.spy(o,'value',{total_rows:0,rows:[]},'Get revision list'); o.jio.allDocs(o.f); o.tick(o); o.spy(o,'value',{total_rows:0,rows:[],conflicts:{total_rows:0,rows:[]}}, 'Get revision list with informations'); o.jio.allDocs({conflicts:true,revs:true,info_revs:true},o.f); o.tick(o); o.spy(o,'jobstatus','done','saving file'); o.jio.put({_id:'file',content:'content file'},function (err,val) { o.rev.file1 = val?val.rev:undefined; o.f(err,val); }); o.tick(o); o.spy(o,'jobstatus','done','saving memo'); o.jio.put({_id:'memo',content:'content memo'},function (err,val) { o.rev.memo1 = val?val.rev:undefined; o.f(err,val); }); o.tick(o); o.spy(o,'status',409,'saving memo conflict'); o.jio.put({_id:'memo',content:'content memo'},function (err,val) { o.rev.memo2 = err?err.rev:undefined; o.f(err,val); }); o.tick(o); o.f = o.t.spy(); o.jio.allDocs(function (err,val) { var i; if (val) { for (i = 0; i < val.total_rows; i+= 1) { val.rows[i].value._creation_date = val.rows[i].value._creation_date?true:undefined; val.rows[i].value._last_modified = val.rows[i].value._last_modified?true:undefined; o.rev[i] = checkRev (val.rows[i].value._rev); } } deepEqual(err||val,{total_rows:2,rows:[{ id:'file',key:'file',value:{ _creation_date:true,_last_modified:true,_rev:o.rev[0] } },{ id:'memo',key:'memo',value:{ _creation_date:true,_last_modified:true,_rev:o.rev[1] } }]},'Get revision list after adding 2 files'); o.f(); }); o.tick(o); o.f = o.t.spy(); o.jio.allDocs( {conflicts:true,revs:true,revs_info:true}, function (err,val) { var i; if (val) { for (i = 0; i < val.total_rows; i+= 1) { val.rows[i].value._creation_date = val.rows[i].value._creation_date?true:undefined; val.rows[i].value._last_modified = val.rows[i].value._last_modified?true:undefined; if (val.conflicts && val.conflicts.rows) { o.solveConflict = checkConflictRow (val.conflicts.rows[0]); } } } deepEqual(err||val,{ total_rows:2,rows:[{ id:'file',key:'file',value:{ _creation_date:true,_last_modified:true, _revisions:{start:1,ids:[getHashFromRev(o.rev.file1)]}, _rev:o.rev.file1,_revs_info:[{ rev:o.rev.file1,status:'available' }] } },{ id:'memo',key:'memo',value:{ _creation_date:true,_last_modified:true, _revisions:{start:1,ids:[getHashFromRev(o.rev.memo2)]}, _rev:o.rev.memo2,_revs_info:[{ rev:o.rev.memo1,status:'available' },{ rev:o.rev.memo2,status:'available' }] } }], conflicts:{total_rows:1,rows:[{ id:'memo',key:[o.rev.memo1,o.rev.memo2], value:{_solveConflict:'function'} }]} },'Get revision list with informations after adding 2 files'); o.f(); }); o.tick(o); o.jio.stop(); }); */ ;(function() { module ('Jio XWikiStorage'); var setUp = function(that, liveTest) { var o = generateTools(that); o.server = sinon.fakeServer.create(); o.jio = JIO.newJio({type:'xwiki',formTokenPath:'form_token'}); o.addFakeServerResponse("xwiki", "GET", "form_token", 200, '<meta name="form_token" content="OMGHAX"/>'); o._addFakeServerResponse = o.addFakeServerResponse; o.expectedRequests = []; o.addFakeServerResponse = function(a,b,c,d,e) { o._addFakeServerResponse(a,b,c,d,e); o.expectedRequests.push([b,c]); }; o.assertReqs = function(count, message) { o.requests = (o.requests || 0) + count; ok(o.server.requests.length === o.requests, message + "[expected [" + count + "] got [" + (o.server.requests.length - (o.requests - count)) + "]]"); for (var i = 1; i <= count; i++) { var req = o.server.requests[o.server.requests.length - i]; if (!req) { break; } for (var j = o.expectedRequests.length - 1; j >= 0; --j) { var expected = o.expectedRequests[j]; if (req.method === expected[0] && req.url.indexOf(expected[1]) !== 0) { o.expectedRequests.splice(j, 1); } } } var ex = o.expectedRequests.pop(); if (ex) { ok(0, "expected [" + ex[0] + "] request for [" + ex[1] + "]"); } }; return o; }; test ("Post", function () { var o = setUp(this); // post without id o.spy (o, "status", 405, "Post without id"); o.jio.post({}, o.f); o.clock.tick(5000); o.assertReqs(0, "no id -> no request"); // post non empty document o.addFakeServerResponse("xwiki", "POST", "myFile", 201, "HTML RESPONSE"); o.spy(o, "value", {"id": "myFile", "ok": true}, "Create = POST non empty document"); o.jio.post({"_id": "myFile", "title": "hello there"}, o.f); o.clock.tick(5000); o.server.respond(); o.assertReqs(3, "put -> 1 request to get csrf token, 1 to get doc and 1 to post data"); // post but document already exists (post = error!, put = ok) o.answer = '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>' + '<page xmlns="http://www.xwiki.org"><title>hello there</title></page>'; o.addFakeServerResponse("xwiki", "GET", "myFile2", 200, o.answer); o.spy (o, "status", 409, "Post but document already exists"); o.jio.post({"_id": "myFile2", "title": "hello again"}, o.f); o.clock.tick(5000); o.server.respond(); o.assertReqs(1, "post w/ existing doc -> 1 request to get doc then fail"); o.jio.stop(); }); test ("Put", function(){ var o = setUp(this); // put without id => id required o.spy (o, "status", 20, "Put without id"); o.jio.put({}, o.f); o.clock.tick(5000); o.assertReqs(0, "put w/o id -> 0 requests"); // put non empty document o.addFakeServerResponse("xwiki", "POST", "put1", 201, "HTML RESPONSE"); o.spy (o, "value", {"ok": true, "id": "put1"}, "Create = PUT non empty document"); o.jio.put({"_id": "put1", "title": "myPut1"}, o.f); o.clock.tick(5000); o.server.respond(); o.assertReqs(3, "put normal doc -> 1 req to get doc, 1 for csrf token, 1 to post"); // put but document already exists = update o.answer = '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>' + '<page xmlns="http://www.xwiki.org"><title>mtPut1</title></page>'; o.addFakeServerResponse("xwiki", "GET", "put2", 200, o.answer); o.addFakeServerResponse("xwiki", "POST", "put2", 201, "HTML RESPONSE"); o.spy (o, "value", {"ok": true, "id": "put2"}, "Updated the document"); o.jio.put({"_id": "put2", "title": "myPut2abcdedg"}, o.f); o.clock.tick(5000); o.server.respond(); o.assertReqs(3, "put update doc -> 1 req to get doc, 1 for csrf token, 1 to post"); o.jio.stop(); }); test ("PutAttachment", function(){ var o = setUp(this); // putAttachment without doc id => id required o.spy(o, "status", 20, "PutAttachment without doc id"); o.jio.putAttachment({}, o.f); o.clock.tick(5000); o.assertReqs(0, "put attach w/o doc id -> 0 requests"); // putAttachment without attachment id => attachment id required o.spy(o, "status", 22, "PutAttachment without attachment id"); o.jio.putAttachment({"_id": "putattmt1"}, o.f); o.clock.tick(5000); o.assertReqs(0, "put attach w/o attach id -> 0 requests"); // putAttachment without underlying document => not found o.addFakeServerResponse("xwiki", "GET", "putattmtx", 404, "HTML RESPONSE"); o.spy(o, "status", 404, "PutAttachment without document"); o.jio.putAttachment({"_id": "putattmtx", "_attachment": "putattmt2"}, o.f); o.clock.tick(5000); o.server.respond(); o.assertReqs(1, "put attach w/o existing document -> 1 request to get doc"); // putAttachment with document without data o.answer = '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>' + '<page xmlns="http://www.xwiki.org"><title>myPutAttm</title></page>'; o.addFakeServerResponse("xwiki", "GET", "putattmt1", 200, o.answer); o.addFakeServerResponse("xwiki", "POST", "putattmt1/putattmt2", 201,"HTML"+ + "RESPONSE"); o.spy(o, "value", {"ok": true, "id": "putattmt1/putattmt2"}, "PutAttachment with document, without data"); o.jio.putAttachment({"_id": "putattmt1", "_attachment": "putattmt2"}, o.f); o.clock.tick(5000); o.server.respond(); o.assertReqs(3, "put attach -> 1 request to get document, 1 to put " + "attach, 1 to get csrf token"); o.jio.stop(); }); test ("Get", function(){ var o = setUp(this); // get inexistent document o.spy(o, "status", 404, "Get non existing document"); o.jio.get("get1", o.f); o.clock.tick(5000); o.server.respond(); o.assertReqs(1, "try to get nonexistent doc -> 1 request"); // get inexistent attachment o.spy(o, "status", 404, "Get non existing attachment"); o.jio.get("get1/get2", o.f); o.clock.tick(5000); o.server.respond(); o.assertReqs(1, "try to get nonexistent attach -> 1 request"); // get document o.answer = '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>' + '<page xmlns="http://www.xwiki.org"><title>some title</title></page>'; o.addFakeServerResponse("xwiki", "GET", "get3", 200, o.answer); o.spy(o, "value", {"_id": "get3", "title": "some title"}, "Get document"); o.jio.get("get3", o.f); o.clock.tick(5000); o.server.respond(); o.assertReqs(1, "get document -> 1 request"); // get inexistent attachment (document exists) o.spy(o, "status", 404, "Get non existing attachment (doc exists)"); o.jio.get({"_id": "get3", "_attachment": "getx"}, o.f); o.clock.tick(5000); o.server.respond(); o.assertReqs(1, "get nonexistant attachment -> 1 request"); // get attachment o.answer = JSON.stringify({"_id": "get4", "title": "some attachment"}); o.addFakeServerResponse("xwiki", "GET", "get3/get4", 200, o.answer); o.spy(o, "value", {"_id": "get4", "title": "some attachment"}, "Get attachment"); o.jio.get({"_id": "get3", "_attachment": "get4"}, o.f); o.clock.tick(5000); o.server.respond(); o.assertReqs(1, "get attachment -> 1 request"); o.jio.stop(); }); test ("Remove", function(){ var o = setUp(this); // remove inexistent document o.addFakeServerResponse("xwiki", "GET", "remove1", 404, "HTML RESPONSE"); o.spy(o, "status", 404, "Remove non existening document"); o.jio.remove({"_id": "remove1"}, o.f); o.clock.tick(5000); o.server.respond(); o.assertReqs(2, "remove nonexistent doc -> 1 request for csrf and 1 for doc"); // remove inexistent document/attachment o.addFakeServerResponse("xwiki", "GET", "remove1/remove2", 404, "HTML" + "RESPONSE"); o.spy(o, "status", 404, "Remove inexistent document/attachment"); o.jio.removeAttachment({"_id": "remove1", "_attachment": "remove2"}, o.f); o.clock.tick(5000); o.server.respond(); o.assertReqs(2, "remove nonexistant attach -> 1 request for csrf and 1 for doc"); // remove document //o.answer = '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>' + // '<page xmlns="http://www.xwiki.org"><title>some doc</title></page>'; //o.addFakeServerResponse("xwiki", "GET", "remove3", 200, o.answer); o.addFakeServerResponse("xwiki", "POST", "bin/delete/Main/remove3", 200, "HTML RESPONSE"); o.spy(o, "value", {"ok": true, "id": "remove3"}, "Remove document"); o.jio.remove({"_id": "remove3"}, o.f); o.clock.tick(5000); o.server.respond(); o.assertReqs(2, "remove document -> 1 request for csrf and 1 for deleting doc"); o.answer = JSON.stringify({ "_id": "remove4", "title": "some doc", "_attachments": { "remove5": { "length": 4, "digest": "md5-d41d8cd98f00b204e9800998ecf8427e" } } }); // remove attachment o.addFakeServerResponse("xwiki", "POST", "delattachment/Main/remove4/remove5", 200, "HTML RESPONSE"); o.spy(o, "value", {"ok": true, "id": "remove4/remove5"}, "Remove attachment"); o.jio.removeAttachment({"_id": "remove4", "_attachment": "remove5"}, o.f); o.clock.tick(5000); o.server.respond(); o.assertReqs(2, "remove attach -> 1 request for csrf and 1 for deletion"); o.jio.stop(); }); /* test ("AllDocs", function () { // need to make server requests before activating fakeServer var davlist = getXML('responsexml/davlist'), o = setUp(this); // get allDocs, no content addFakeServerResponse("xwiki", "PROPFIND", "", 200, davlist); o.thisShouldBeTheAnswer = { "rows": [ {"id": "alldocs1", "key": "alldocs1", "value": {}}, {"id": "alldocs2", "key": "alldocs2", "value": {}} ], "total_rows": 2 } o.spy(o, "value", o.thisShouldBeTheAnswer, "allDocs (no content)"); o.jio.allDocs(o.f); o.clock.tick(5000); respond(); // allDocs with option include o.all1 = {"_id": "allDocs1", "title": "a doc title"}; o.all2 = {"_id": "allDocs2", "title": "another doc title"}; o.thisShouldBeTheAnswer = { "rows": [ {"id": "alldocs1", "key": "alldocs1", "value": {}, "doc": o.all1}, {"id": "alldocs2", "key": "alldocs2", "value": {}, "doc": o.all2} ], "total_rows": 2 } addFakeServerResponse("xwiki", "GET", "alldocs1", 200, JSON.stringify(o.all1)); addFakeServerResponse("xwiki", "GET", "alldocs2", 200, JSON.stringify(o.all2)); o.spy(o, "value", o.thisShouldBeTheAnswer, "allDocs (include_docs)"); o.jio.allDocs({"include_docs":true}, o.f); o.clock.tick(5000); respond(); o.jio.stop(); }); */ module("Amazon S3 Storage"); /* Post without id Create = POST non empty document Post but document already exists */ test ("Post", function(){ var o = generateTools(this); o.jio = JIO.newJio({ "type":"s3", "AWSIdentifier":"dontcare", "password":"dontcare", "server":"jiobucket", "url":"https://jiobucket.s3.amazonaws.com" }); o.server = sinon.fakeServer.create(); //Post without ID (ID is generated by storage) o.server.respondWith("GET", "https://jiobucket.s3.amazonaws.com/", [404, {}, "HTML Response"] ); o.server.respondWith("POST", "https://jiobucket.s3.amazonaws.com/", [204, {}, "HTML Response"] ); //o.spy (o, "status", 405, "Post without id"); o.spy(o, "jobstatus", "done", "Post without ID"); o.jio.post({}, o.f); o.clock.tick(1000); o.server.respond(); o.tick(o); o.server.restore(); //Post with ID o.server = sinon.fakeServer.create(); o.server.respondWith("GET", "https://jiobucket.s3.amazonaws.com/post1", [404, {}, "HTML Response"] ); o.server.respondWith("POST", "https://jiobucket.s3.amazonaws.com/", [204, {}, "HTML Response"] ); o.spy(o, "value", {'ok':true,'id':'post1'}, "Post with ID"); o.jio.post({"_id": "post1", "title": "myPost1"}, o.f); o.clock.tick(1000); o.server.respond(); o.tick(o); o.server.restore(); //Post but document already exists (update) o.server = sinon.fakeServer.create(); o.server.respondWith("GET", "http://s3.amazonaws.com/jiobucket/post2", [200, {}, "HTML Response"] ); o.spy (o, "status", 409, "Post but document already exists (update)"); //o.spy(o, "jobstatus", "done", "Post without ID"); o.jio.post({"_id": "post2", "title": "myPost2"}, o.f); o.clock.tick(1000); o.server.respond(); o.tick(o); o.server.restore(); o.jio.stop(); }); /* Put without id Create = PUT non empty document Updated the document */ test("Put", function(){ var o = generateTools(this); o.jio = JIO.newJio({ "type":"s3", "AWSIdentifier":"dontcare", "password":"dontcare", "server":"jiobucket", "url":"http://s3.amazonaws.com/jiobucket/put1" }); // put without id => id required o.server = sinon.fakeServer.create(); o.spy (o, "status", 20, "Put without id"); o.jio.put({}, o.f); o.clock.tick(5000); o.server.respond(); o.tick(o); o.server.restore(); //Put non empty document o.server = sinon.fakeServer.create(); o.server.respondWith("GET", "http://s3.amazonaws.com/jiobucket/http:%252F%252F100%2525_.json", [404, {}, "HTML Response"] ); o.server.respondWith("PUT", "http://s3.amazonaws.com/jiobucket/http:%252F%252F100%2525_.json", [200, {}, "HTML Response"] ); o.spy (o, "value", {"ok": true, "id": "http://100%.json"}, "PUT non empty document"); o.jio.put({"_id": "http://100%.json", "title": "myPut1"}, o.f); o.clock.tick(5000); o.server.respond(); o.tick(o); o.server.restore(); //Put an existing document (update) o.server = sinon.fakeServer.create(); o.server.respondWith("GET", "http://s3.amazonaws.com/jiobucket/http:%252F%252F100%2525_.json", [200, {}, "HTML Response"] ); o.server.respondWith("PUT", "http://s3.amazonaws.com/jiobucket/http:%252F%252F100%2525_.json", [200, {}, "HTML Response"] ); o.spy (o, "value", {"ok": true, "id": "http://100%.json"}, "PUT non empty document"); o.jio.put({"_id": "http://100%.json", "title": "myPut1"}, o.f); o.clock.tick(1000); o.server.respond(); o.tick(o); o.server.restore(); o.jio.stop(); }); test ("PutAttachment", function(){ var o = generateTools(this); o.jio = JIO.newJio({ "type":"s3", "AWSIdentifier":"dontcare", "password":"dontcare", "server":"jiobucket", "url":"https://jiobucket.s3.amazonaws.com" }); //PutAttachment without document ID (document ID is required) o.server = sinon.fakeServer.create(); //PutAttachment without attachment ID (attachment ID is required) o.spy(o, "status", 20, "PutAttachment without doc id -> 20"); o.jio.putAttachment({"_attachment": "putattmt1"}, o.f); o.tick(o); // putAttachment without attachment id => 22 Attachment Id Required o.spy(o, "status", 22, "PutAttachment without attachment id -> 22"); o.jio.putAttachment({"_id": "http://100%.json"}, o.f); o.tick(o); //PutAttachment without underlying document (document is required) o.server = sinon.fakeServer.create(); o.server.respondWith("GET", "http://s3.amazonaws.com/jiobucket/http:%252F%252F100%2525_.json", [404, {}, "HTML Response"] ); o.spy(o, "status", 404, "PutAttachment without document -> 404"); o.jio.putAttachment({ "_id": "http://100%.json", "_attachment": "putattmt2" }, {"max_retry": 1}, o.f); o.clock.tick(1000); o.server.respond(); o.tick(o); o.server.restore(); //PutAttachment o.server = sinon.fakeServer.create(); o.server.respondWith("GET", "http://s3.amazonaws.com/jiobucket/http:%252F%252F100%2525_.json", [200,{"Content-Type": "text/plain"},'{"_id":"http://100%.json","title":"Hi There!"}'] ); o.server.respondWith("PUT", "http://s3.amazonaws.com/jiobucket/http:%252F%252F100%2525_.json", [200, {}, "HTML Response"] ); o.server.respondWith("PUT", "http://s3.amazonaws.com/jiobucket/http:%252F%252F100%2525_.json.body_.html", [200, {}, "HTML Response"] ); o.spy(o, "value", { "ok": true, "id": "http://100%.json", "attachment": "body.html" }, "PutAttachment"); o.jio.putAttachment({ "_id": "http://100%.json", "_attachment": "body.html", "_mimetype": "text/html", "_data": "<h1>Hi There!!</h1><p>How are you?</p>" }, {"max_retry": 1}, o.f); o.clock.tick(1000); o.server.respond(); o.tick(o); o.server.restore(); o.jio.stop(); }); /* Get non existing document Get non existing attachment Get document Get non existing attachment (doc exists) Get attachment */ test("Get", function(){ var o = generateTools(this); o.jio = JIO.newJio({ "type":"s3", "AWSIdentifier":"dontcare", "password":"dontcare", "server":"jiobucket", "url":"https://jiobucket.s3.amazonaws.com" }); //Get non existing document o.server = sinon.fakeServer.create(); o.server.respondWith("GET", "https://jiobucket.s3.amazonaws.com/doc", [404, {}, "HTML Response"] ); o.spy(o, "status", 404, "Get non existing document" ); o.jio.get("doc", {"max_retry":1}, o.f); o.clock.tick(5000); o.server.respond(); o.tick(o); o.server.restore(); //Get document o.server = sinon.fakeServer.create(); o.server.respondWith("GET", "http://s3.amazonaws.com/jiobucket/http:%252F%252F100%2525_.json", [200,{"Content-Type": "text/plain"},'{"_id":"http://100%.json","title":"Hi There!"}'] ); o.spy(o, "value", {"_id":"http://100%.json","title":"Hi There!"}, "Get document" ) o.jio.get({"_id":"http://100%.json"}, {"max_retry":1}, o.f); o.clock.tick(5000); o.server.respond(); o.tick(o); o.server.restore(); o.jio.stop(); }); test("GetAttachment", function(){ var o = generateTools(this); o.jio = JIO.newJio({ "type":"s3", "AWSIdentifier":"dontcare", "password":"dontcare", "server":"jiobucket", "url":"https://jiobucket.s3.amazonaws.com" }); //Get non existing attachment o.server = sinon.fakeServer.create(); o.server.respondWith("GET", "http://s3.amazonaws.com/jiobucket/http:%252F%252F100%2525_.json.body_.html", [404, {}, "HTML Response"] ); o.spy(o, "status", 404, "Get non existing attachment" ); o.jio.getAttachment({ "_id": "http://100%.json", "_attachment": "body.html" }, {"max_retry": 1}, o.f); o.clock.tick(5000); o.server.respond(); o.tick(o); o.server.restore(); //Get attachment o.server = sinon.fakeServer.create(); o.server.respondWith("GET", "http://s3.amazonaws.com/jiobucket/http:%252F%252F100%2525_.json.body_.html", [200, {"Content-Type": "text/plain"}, "My Attachment Content"] ); o.spy(o, "value", "My Attachment Content", "Get attachment"); o.jio.getAttachment({ "_id": "http://100%.json", "_attachment": "body.html" }, {"max_retry": 1}, o.f); o.clock.tick(5000); o.server.respond(); o.tick(o); o.server.restore(); o.jio.stop(); }); //begins the remove tests /* Remove inexistent document Remove inexistent document/attachment Remove document Check index file Check if document has been removed Remove one of multiple attachment Check index file Remove one document and attachment together Check index file Check if attachment has been removed Check if document has been removed */ test ("Remove", function(){ var o = generateTools(this); o.jio = JIO.newJio({ "type":"s3", "AWSIdentifier":"dontcare", "password":"dontcare", "server":"jiobucket", "url":"https://jiobucket.s3.amazonaws.com/" }); //Remove non-existing document o.server = sinon.fakeServer.create(); o.server.respondWith("DELETE", "http://s3.amazonaws.com/jiobucket/http:%252F%252F100%2525_.json", [404, {}, "HTML RESPONSE"] ); o.spy(o, "status", 404, "Remove non existing document"); o.jio.remove({"_id": "http://100%.json"}, o.f); o.clock.tick(5000); o.server.respond(); o.server.restore(); //Remove document o.server = sinon.fakeServer.create(); o.server.respondWith("DELETE", "http://s3.amazonaws.com/jiobucket/http:%252F%252F100%2525_.json", [200, {"Content-Type": "text/plain"}, "My Attachment Content"] ); o.spy(o, "value", {"ok":true, "id":"http://100%.json"}, "Remove document"); o.jio.remove({"_id": "http://100%.json"}, o.f); o.clock.tick(5000); o.server.respond(); o.tick(o); o.server.restore(); //Remove document with multiple attachments o.server = sinon.fakeServer.create(); o.server.respondWith("GET", "http://s3.amazonaws.com/jiobucket/http:%252F%252F100%2525_.json", [ 200, {"Content-Type": "text/html"}, JSON.stringify({ "_attachments": { "body.html": { "length": 32, "digest": "md5-dontcare", "content_type": "text/html" }, "other": { "length": 3, "digest": "md5-dontcare-again", "content_type": "text/plain" } } }) ] ); o.server.respondWith("DELETE", "http://s3.amazonaws.com/jiobucket/http:%252F%252F100%2525_.json", [200, {"Content-Type": "text/plain"}, "<h1>Deleted</h1>"] ); o.server.respondWith("DELETE", "http://s3.amazonaws.com/jiobucket/http:%252F%252F100%2525_.json.body_.html", [200, {"Content-Type": "text/plain"}, "<h1>Deleted</h1>"] ); o.server.respondWith("DELETE", "http://s3.amazonaws.com/jiobucket/http:%252F%252F100%2525_.json.other", [200, {"Content-Type": "text/plain"}, "<h1>Deleted</h1>"] ); o.spy(o, "value", {"ok": true, "id": "http://100%.json"}, "Remove document containing multiple attachments"); o.jio.remove({"_id": "http://100%.json"}, {"max_retry": 1}, o.f); o.clock.tick(1000); o.server.respond(); o.tick(o); o.server.restore(); o.jio.stop(); }); test ("RemoveAttachment", function(){ var o = generateTools(this); o.jio = JIO.newJio({ "type":"s3", "AWSIdentifier":"dontcare", "password":"dontcare", "server":"jiobucket", "url":"https://jiobucket.s3.amazonaws.com/" }); //Remove non-existing attachment o.server = sinon.fakeServer.create(); o.server.respondWith("DELETE", "http://s3.amazonaws.com/jiobucket/http:%252F%252F100%2525_.json.body_.html", [404, {}, "HTML RESPONSE"] ); o.spy(o, "status", 404, "Remove non existing attachment"); o.jio.removeAttachment({ "_id": "http://100%.json", "_attachment": "body.html" }, {"max_retry": 1}, o.f); o.clock.tick(5000); o.server.respond(); o.server.restore(); //Remove attachment o.server = sinon.fakeServer.create(); o.server.respondWith("GET", "http://s3.amazonaws.com/jiobucket/http:%252F%252F100%2525_.json", [200,{"Content-Type": "text/plain"},'{"_id":"http://100%.json","title":"Hi There!"}'] ); o.server.respondWith("PUT", "http://s3.amazonaws.com/jiobucket/http:%252F%252F100%2525_.json", [200,{"Content-Type": "text/plain"},'{"_id":"http://100%.json","title":"Hi There!"}'] ); o.server.respondWith("DELETE", "http://s3.amazonaws.com/jiobucket/http:%252F%252F100%2525_.json.body_.html", [200, {"Content-Type": "text/plain"}, "My Attachment Content"] ); o.spy(o, "value", { "ok": true, "id": "http://100%.json", "attachment": "body.html" }, "Remove attachment"); o.jio.removeAttachment({ "_id": "http://100%.json", "_attachment": "body.html" }, {"max_retry": 1}, o.f); o.clock.tick(5000); o.server.respond(); o.tick(o); o.server.restore(); o.jio.stop(); }); test ("AllDocs", function(){ var o = generateTools(this); o.jio = JIO.newJio({ "type":"s3", "AWSIdentifier":"dontcare", "password":"dontcare", "server":"jiobucket", "url":"https://jiobucket.s3.amazonaws.com/" }); //allDocs without option o.server = sinon.fakeServer.create(); o.server.respondWith("GET", "http://s3.amazonaws.com/jiobucket/", [204, {}, '<?xml version="1.0" encoding="UTF-8"?><ListBucketResult xmlns="http://s3.amazonaws.com/doc/2006-03-01/"><Name>jiobucket</Name><Prefix></Prefix><Marker></Marker><MaxKeys>1000</MaxKeys><IsTruncated>false</IsTruncated><Contents><Key>documentONE</Key><LastModified>2013-05-03T15:32:01.000Z</LastModified><ETag>"8a65389818768e1f5e6530a949233581"</ETag><Size>163</Size><Owner><ID>5d09e586ab92acad85e9d053f769cce65f82178e218d9ac9b0473f3ce7f89606</ID><DisplayName>jonathan.rivalan</DisplayName></Owner><StorageClass>STANDARD</StorageClass></Contents><Contents><Key>documentONE.1st_Attachment_manual</Key><LastModified>2013-05-03T15:32:02.000Z</LastModified><ETag>"f391dec65366d2b470406bc7b9595dea"</ETag><Size>35</Size><Owner><ID>5d09e586ab92acad85e9d053f769cce65f82178e218d9ac9b0473f3ce7f89606</ID><DisplayName>jonathan.rivalan</DisplayName></Owner><StorageClass>STANDARD</StorageClass></Contents></ListBucketResult>'] ); o.spy(o, "jobstatus", "done", "AllDocs without include docs"); o.jio.allDocs(o.f); o.clock.tick(5000); o.server.respond(); o.tick(o); o.server.restore(); //allDocs with the include docs option o.server = sinon.fakeServer.create(); o.server.respondWith("GET", "http://s3.amazonaws.com/jiobucket/", [204, {}, '<?xml version="1.0" encoding="UTF-8"?><ListBucketResult xmlns="http://s3.amazonaws.com/doc/2006-03-01/"><Name>jiobucket</Name><Prefix></Prefix><Marker></Marker><MaxKeys>1000</MaxKeys><IsTruncated>false</IsTruncated><Contents><Key>documentONE</Key><LastModified>2013-05-03T15:32:01.000Z</LastModified><ETag>"8a65389818768e1f5e6530a949233581"</ETag><Size>163</Size><Owner><ID>5d09e586ab92acad85e9d053f769cce65f82178e218d9ac9b0473f3ce7f89606</ID><DisplayName>jonathan.rivalan</DisplayName></Owner><StorageClass>STANDARD</StorageClass></Contents><Contents><Key>documentONE.1st_Attachment_manual</Key><LastModified>2013-05-03T15:32:02.000Z</LastModified><ETag>"f391dec65366d2b470406bc7b9595dea"</ETag><Size>35</Size><Owner><ID>5d09e586ab92acad85e9d053f769cce65f82178e218d9ac9b0473f3ce7f89606</ID><DisplayName>jonathan.rivalan</DisplayName></Owner><StorageClass>STANDARD</StorageClass></Contents></ListBucketResult>'] ); o.server.respondWith("GET", "http://jiobucket.s3.amazonaws.com/documentONE", [ 200, {"Content-Type": "text/html"}, JSON.stringify({ "_attachments": { "body.html": { "length": 32, "digest": "md5-dontcare", "content_type": "text/html" }, "other": { "length": 3, "digest": "md5-dontcare-again", "content_type": "text/plain" } } }) ] ); console.log(o); o.spy(o, "jobstatus", "done", "AllDocs with include docs"); o.jio.allDocs({"include_docs": true},o.f); console.log(o.f); o.clock.tick(5000); o.server.respond(); o.tick(o); o.server.restore(); o.jio.stop(); }); var nThen = function(next) { var funcs = []; var calls = 0; var waitFor = function(func) { calls++; return function() { if (func) { func.apply(null, arguments); } calls = (calls || 1) - 1; while (!calls && funcs.length) { funcs.shift()(waitFor); } }; }; next(waitFor); var ret = { nThen: function(next) { funcs.push(next); return ret; }, orTimeout: function(func, milliseconds) { var cto; var timeout = setTimeout(function() { while (funcs.shift() !== cto) ; func(waitFor); calls = (calls || 1) - 1; while (!calls && funcs.length) { console.log("call"); funcs.shift()(waitFor); } }, milliseconds); funcs.push(cto = function() { clearTimeout(timeout); }); return ret; } }; return ret; }; if (window.location.href.match(/xwiki\/bin\/view/)) (function() { // This test will only be run if we are inside of a live XWiki instance. test ("XWiki Live Server setup", function () { var o = setUp(this); o.jio.stop(); this.sandbox.restore(); o.server.restore(); o.jio.start(); QUnit.stop(); nThen(function(waitFor) { // Remove the document if it exists. o.jio.remove({"_id": "one.json"}, waitFor()); }).nThen(function(waitFor) { // post a new document o.spy(o, "value", {"id": "one.json", "ok": true}, "Live post document"); o.jio.post({"_id": "one.json", "title": "hello"}, waitFor(o.f)); }).nThen(function(waitFor) { o.jio.get("one.json", waitFor(function(err, ret) { ok(!err); ok(ret._id == "one.json"); ok(ret.title == "hello"); })); }).nThen(function(waitFor) { // modify document o.spy(o, "value", {"id": "one.json", "ok": true}, "Live modify document"); o.jio.put({"_id": "one.json", "title": "hello modified"}, waitFor(o.f)); }).nThen(function(waitFor) { o.jio.get("one.json", waitFor(function(err, ret) { ok(!err); ok(ret.title == "hello modified"); })); }).nThen(function(waitFor) { // add attachment o.spy(o, "value", {"id": "one.json/att.txt", "ok": true}, "Put attachment"); o.jio.putAttachment({ "_id": "one.json", "_attachment": "att.txt", "_mimetype": "text/plain", "_data": "there2" }, waitFor(o.f)); }).nThen(function(waitFor) { // test allDocs /*o.jio.allDocs({"include_docs":true}, function(s){console.log(s);}, function ( e ) {console.log(e); }, o.f);*/ }).nThen(function(waitFor) { // get Attachment o.jio.getAttachment({"_id":"one.json", "_attachment":"att.txt"}, waitFor(function(err, ret) { ok(!err); ok(ret == "there2"); })); }).nThen(function(waitFor) { // remove Attachment o.spy(o, "value", {"id": "one.json/att.txt", "ok": true}, "Remove attachment"); o.jio.removeAttachment({"_id":"one.json","_attachment":"att.txt"}, waitFor(o.f)); }).nThen(function(waitFor) { // remove Document o.spy(o, "value", {"id": "one.json", "ok": true}, "Remove document"); o.jio.remove("one.json", waitFor(o.f)); }).nThen(function(waitFor) { //console.log("success"); }).orTimeout(function() { //console.log("failed"); ok(0); }, 15000).nThen(function() { //console.log("complete"); o.jio.stop(); QUnit.start(); }); }); })(); // Live XWiki })(); // xwiki module("SplitStorage + LocalStorage"); test("Post", function () { var o = generateTools(this); o.jio = JIO.newJio({ "type": "split", "storage_list": [{ "type": "local", "username": "splitstorage", "application_name": "post1" }, { "type": "local", "username": "splitstorage", "application_name": "post2" }] }); // post without id o.spy(o, "jobstatus", "done", "Post document without id"); o.jio.post({ "_underscored_meta": "uvalue", "meta": "data" }, function (err, response) { o.f(err, response); o.uuid = (err || response).id; ok(isUuid(o.uuid), "Uuid should look like " + "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx : " + o.uuid); }); o.tick(o); // check uploaded documents deepEqual( localstorage.getItem('jio/localstorage/splitstorage/post1/' + o.uuid), {"_id": o.uuid, "_underscored_meta": "uvalue", "data": "{\"meta\""}, "Check uploaded document in sub storage 1" ); deepEqual( localstorage.getItem('jio/localstorage/splitstorage/post2/' + o.uuid), {"_id": o.uuid, "_underscored_meta": "uvalue", "data": ":\"data\"}"}, "Check uploaded document in sub storage 2" ); // post with id o.spy(o, "value", {"ok": true, "id": "one"}, "Post document with id"); o.jio.post({ "_id": "one", "_underscored_meta": "uvalue", "meta": "data", "hello": "world" }, o.f); o.tick(o); // check uploaded documents deepEqual(localstorage.getItem('jio/localstorage/splitstorage/post1/one'), { "_id": "one", "_underscored_meta": "uvalue", "data": "{\"meta\":\"data\"," }, "Check uploaded document in sub storage 1"); deepEqual(localstorage.getItem('jio/localstorage/splitstorage/post2/one'), { "_id": "one", "_underscored_meta": "uvalue", "data": "\"hello\":\"world\"}" }, "Check uploaded document in sub storage 2"); // post with id o.spy(o, "status", 409, "Post document with same id"); o.jio.post({ "_id": "one", "_underscored_meta": "uvalue", "meta": "data", "hello": "world" }, o.f); o.tick(o); o.jio.stop(); }); test("PutAttachment", function () { var o = generateTools(this); o.jio = JIO.newJio({ "type": "split", "storage_list": [{ "type": "local", "username": "splitstorage", "application_name": "putAttachment1" }, { "type": "local", "username": "splitstorage", "application_name": "putAttachment2" }] }); o.spy(o, "status", 404, "Put attachment on a inexistent document"); o.jio.putAttachment({ "_id": "one", "_attachment": "my_attachment", "_data": "My Data", "_mimetype": "text/plain" }, o.f); o.tick(o); o.jio.post({"_id": "one", "_underscored_meta": "uvalue", "meta": "data"}); o.clock.tick(1000); o.spy(o, "value", { "ok": true, "id": "one", "attachment": "my_attachment" }, "Put attachment"); o.jio.putAttachment({ "_id": "one", "_attachment": "my_attachment", "_data": "My Data", "_mimetype": "text/plain" }, o.f); o.tick(o); // check uploaded documents deepEqual(localstorage.getItem( 'jio/localstorage/splitstorage/putAttachment1/one' ), { "_id": "one", "_underscored_meta": "uvalue", "data": "{\"meta\"", "_attachments": { "my_attachment": { "length": 3, "digest": "md5-1b4686bc8ca15befdccb1da1dcb8c271", // md5("My ") "content_type": "text/plain" } } }, "Check uploaded document in sub storage 1"); deepEqual(localstorage.getItem( 'jio/localstorage/splitstorage/putAttachment2/one' ), { "_id": "one", "_underscored_meta": "uvalue", "data": ":\"data\"}", "_attachments": { "my_attachment": { "length": 4, "digest": "md5-f6068daa29dbb05a7ead1e3b5a48bbee", // md5("Data") "content_type": "text/plain" } } }, "Check uploaded document in sub storage 2"); deepEqual(localstorage.getItem( 'jio/localstorage/splitstorage/putAttachment1/one/my_attachment' ), "My ", "Check uploaded document in sub storage 1"); deepEqual(localstorage.getItem( 'jio/localstorage/splitstorage/putAttachment2/one/my_attachment' ), "Data", "Check uploaded document in sub storage 2"); o.jio.stop(); }); test("Get", function () { var o = generateTools(this); o.jio = JIO.newJio({ "type": "split", "storage_list": [{ "type": "local", "username": "splitstorage", "application_name": "get1" }, { "type": "local", "username": "splitstorage", "application_name": "get2" }] }); o.spy(o, "status", 404, "Get missing document"); o.jio.get({"_id": "one"}, o.f); o.tick(o); o.jio.post({"_id": "one", "_underscored_meta": "uvalue", "meta": "data"}); o.clock.tick(1000); o.spy(o, "value", { "_id": "one", "_underscored_meta": "uvalue", "meta": "data" }, "Get posted document"); o.jio.get({"_id": "one"}, o.f); o.tick(o); o.jio.putAttachment({ "_id": "one", "_attachment": "my_attachment", "_data": "My Data", "_mimetype": "text/plain" }); o.clock.tick(1000); o.spy(o, "value", { "_id": "one", "_underscored_meta": "uvalue", "meta": "data", "_attachments": { "my_attachment": { "length": 7, "content_type": "text/plain" } } }, "Get document with attachment informations"); o.jio.get({"_id": "one"}, o.f); o.tick(o); o.jio.stop(); }); test("GetAttachment", function () { var o = generateTools(this); o.jio = JIO.newJio({ "type": "split", "storage_list": [{ "type": "local", "username": "splitstorage", "application_name": "getAttachment1" }, { "type": "local", "username": "splitstorage", "application_name": "getAttachment2" }] }); o.spy(o, "status", 404, "Get attachment from missing document"); o.jio.getAttachment({"_id": "one", "_attachment": "my_attachment"}, o.f); o.tick(o); o.jio.post({"_id": "one", "_underscored_meta": "uvalue", "meta": "data"}); o.clock.tick(1000); o.spy(o, "status", 404, "Get missing attachment from document"); o.jio.getAttachment({"_id": "one", "_attachment": "my_attachment"}, o.f); o.tick(o); o.jio.putAttachment({ "_id": "one", "_attachment": "my_attachment", "_data": "My Data", "_mimetype": "text/plain" }); o.clock.tick(1000); o.spy(o, "value", "My Data", "Get attachment"); o.jio.getAttachment({"_id": "one", "_attachment": "my_attachment"}, o.f); o.tick(o); o.jio.stop(); }); test("removeAttachment", function () { var o = generateTools(this); o.jio = JIO.newJio({ "type": "split", "storage_list": [{ "type": "local", "username": "splitstorage", "application_name": "removeAttachment1" }, { "type": "local", "username": "splitstorage", "application_name": "removeAttachment2" }] }); o.spy(o, "status", 404, "Remove attachment from inexistent document"); o.jio.removeAttachment({"_id": "one", "_attachment": "my_attachment"}, o.f); o.tick(o); o.jio.post({"_id": "one", "_underscored_meta": "uvalue", "meta": "data"}); o.clock.tick(1000); o.spy(o, "status", 404, "Remove inexistent attachment"); o.jio.removeAttachment({"_id": "one", "_attachment": "my_attachment"}, o.f); o.tick(o); o.jio.putAttachment({ "_id": "one", "_attachment": "my_attachment", "_data": "My Data", "_mimetype": "text/plain" }); o.clock.tick(1000); o.spy(o, "value", { "ok": true, "id": "one", "attachment": "my_attachment" }, "Remove attachment"); o.jio.removeAttachment({"_id": "one", "_attachment": "my_attachment"}, o.f); o.tick(o); o.spy(o, "value", { "_id": "one", "_underscored_meta": "uvalue", "meta": "data" }, "Get document for check"); o.jio.get({"_id": "one"}, o.f); o.tick(o); o.spy(o, "status", 404, "Get attachment for check"); o.jio.getAttachment({"_id": "one", "_attachment": "my_attachment"}, o.f); o.tick(o); o.jio.stop(); }); test("remove", function () { var o = generateTools(this); o.jio = JIO.newJio({ "type": "split", "storage_list": [{ "type": "local", "username": "splitstorage", "application_name": "remove1" }, { "type": "local", "username": "splitstorage", "application_name": "remove2" }] }); o.spy(o, "status", 404, "Remove missing document"); o.jio.remove({"_id": "one"}, o.f); o.tick(o); o.jio.post({"_id": "one", "_underscored_meta": "uvalue", "meta": "data"}); o.clock.tick(1000); o.jio.putAttachment({ "_id": "one", "_attachment": "my_attachment", "_data": "My Data", "_mimetype": "text/plain" }); o.clock.tick(1000); o.spy(o, "value", {"ok": true, "id": "one"}, "Remove document"); o.jio.remove({"_id": "one"}, o.f); o.tick(o); o.spy(o, "status", 404, "Get attachment for check"); o.jio.getAttachment({"_id": "one", "_attachment": "my_attachment"}, o.f); o.tick(o); o.spy(o, "status", 404, "Get document for check"); o.jio.get({"_id": "one"}, o.f); o.tick(o); o.jio.stop(); }); test("Put", function () { var o = generateTools(this); o.jio = JIO.newJio({ "type": "split", "storage_list": [{ "type": "local", "username": "splitstorage", "application_name": "put1" }, { "type": "local", "username": "splitstorage", "application_name": "put2" }] }); o.spy(o, "value", {"ok": true, "id": "one"}, "Put document"); o.jio.put({"_id": "one", "_underscored_meta": "uvalue", "meta": "data"}, o.f); o.tick(o) o.spy(o, "value", { "_id": "one", "_underscored_meta": "uvalue", "meta": "data" }, "Get document for check"); o.jio.get({"_id": "one"}, o.f); o.tick(o); o.spy(o, "value", {"ok": true, "id": "one"}, "Put document again"); o.jio.put({"_id": "one", "_underscored_meta": "uvalue", "meow": "dog"}, o.f); o.tick(o) o.spy(o, "value", { "_id": "one", "_underscored_meta": "uvalue", "meow": "dog" }, "Get document for check"); o.jio.get({"_id": "one"}, o.f); o.tick(o); o.jio.stop(); }); test("AllDocs", function () { var o = generateTools(this); o.jio = JIO.newJio({ "type": "split", "storage_list": [{ "type": "local", "username": "splitstorage", "application_name": "alldocs1" }, { "type": "local", "username": "splitstorage", "application_name": "alldocs2" }] }); for (o.i = 0; o.i < 5; o.i += 1) { o.jio.post({ "_id": "doc" + o.i, "_underscored_meta": "uvalue" + o.i, "meta": "data" + o.i }); o.clock.tick(1000); } for (o.i = 0; o.i < 2; o.i += 1) { o.jio.putAttachment({ "_id": "doc" + o.i, "_attachment": "my_attachment" + o.i, "_data": "My Data" + o.i, "_mimetype": "text/plain" }); o.clock.tick(1000); } o.spy(o, "value", { "_id": "doc1", "_underscored_meta": "uvalue1", "meta": "data1", "_attachments": { "my_attachment1": { "length": 8, "content_type": "text/plain" } } }, "Get document for check"); o.jio.get({"_id": "doc1"}, o.f); o.tick(o); o.spy(o, "value", { "total_rows": 5, "rows": [{ "id": "doc0", "key": "doc0", "value": {} }, { "id": "doc1", "key": "doc1", "value": {} }, { "id": "doc2", "key": "doc2", "value": {} }, { "id": "doc3", "key": "doc3", "value": {} }, { "id": "doc4", "key": "doc4", "value": {} }] }, "AllDocs with document ids only"); o.jio.allDocs(function (err, response) { if (response && Array.isArray(response.rows)) { response.rows.sort(function (a, b) { return a.id < b.id ? -1 : a.id > b.id ? 1 : 0; }); } o.f(err, response); }); o.tick(o); o.jio.stop(); }); }; // end thisfun if (window.requirejs) { require.config ({ paths: { jiotestsloader: './jiotests.loader', jQueryAPI: '../lib/jquery/jquery', jQuery: '../js/jquery.requirejs_module', JIO: '../src/jio', JIODummyStorages: '../src/jio.dummystorages', JIOStorages: '../src/jio.storage', SJCLAPI:'../lib/sjcl/sjcl.min', SJCL:'../js/sjcl.requirejs_module' } }); require(['jiotestsloader'],thisfun); } else { thisfun ({JIO:jIO}); } }());