Commit f2943ed8 authored by Tristan Cavelier's avatar Tristan Cavelier

replicaterevisionstorage upgraded + tests

parent 5a50c92f
...@@ -22,7 +22,7 @@ ...@@ -22,7 +22,7 @@
module(jIO); module(jIO);
}(['jio'], function (jIO) { }(['jio'], function (jIO) {
"use strict"; "use strict";
jIO.addStorageType('replicaterevision', function (spec) { jIO.addStorage('replicaterevision', function (spec) {
var that = this, priv = {}; var that = this, priv = {};
spec = spec || {}; spec = spec || {};
...@@ -217,16 +217,9 @@ ...@@ -217,16 +217,9 @@
command.success(); command.success();
} }
if (!param._id) { if (!param._id) {
return callback({ return callback({"status": 501});
"status": 501
});
} }
priv.check( priv.check(command, param, option, callback);
command,
param,
option,
callback
);
}; };
/** /**
...@@ -264,8 +257,18 @@ ...@@ -264,8 +257,18 @@
callback = callback || priv.emptyFunction; callback = callback || priv.emptyFunction;
option = option || {}; option = option || {};
functions.begin = function () { functions.begin = function () {
// }; // // XXX make revision storage check and repair
// functions.repairAllSubStorages = function () { // // to enable check/repair sub storage from this storage
// // by calling this function just below
// //functions.repairAllSubStorages();
// // else we assume that sub storages are good
// functions.getAllDocuments(functions.newParam(
// doc,
// option,
// repair
// ));
// };
// functions.repairAllSubStorages = function () {
var i; var i;
for (i = 0; i < priv.storage_list.length; i += 1) { for (i = 0; i < priv.storage_list.length; i += 1) {
priv.send( priv.send(
...@@ -315,8 +318,8 @@ ...@@ -315,8 +318,8 @@
// 1: [responseB, [2]] // 1: [responseB, [2]]
], ],
"attachments": { "attachments": {
// attachmentA : {_id: attachmentA, _revs_info, _mimetype: ..} // attachmentA : {_id: attachmentA, _revs_info, _content_type: ..}
// attachmentB : {_id: attachmentB, _revs_info, _mimetype: ..} // attachmentB : {_id: attachmentB, _revs_info, _content_type: ..}
} }
}, },
"conflicts": { "conflicts": {
...@@ -333,9 +336,9 @@ ...@@ -333,9 +336,9 @@
var i, metadata, cloned_option; var i, metadata, cloned_option;
metadata = priv.clone(param.doc); metadata = priv.clone(param.doc);
cloned_option = priv.clone(param.option); cloned_option = priv.clone(param.option);
option.conflicts = true; cloned_option.conflicts = true;
option.revs = true; cloned_option.revs = true;
option.revs_info = true; cloned_option.revs_info = true;
for (i = 0; i < priv.storage_list.length; i += 1) { for (i = 0; i < priv.storage_list.length; i += 1) {
// if the document is not loaded // if the document is not loaded
priv.send(command, "get", i, priv.send(command, "get", i,
...@@ -356,7 +359,7 @@ ...@@ -356,7 +359,7 @@
// get document failed, exit // get document failed, exit
param.deal_result_state = "error"; param.deal_result_state = "error";
callback({ callback({
"status": "conflict", "status": 409,
"message": "An error occured on the sub storage", "message": "An error occured on the sub storage",
"reason": err.reason "reason": err.reason
}, undefined); }, undefined);
...@@ -378,6 +381,7 @@ ...@@ -378,6 +381,7 @@
// this is now the last response // this is now the last response
functions.makeResponsesStats(param.responses); functions.makeResponsesStats(param.responses);
//console.log(JSON.parse(JSON.stringify(param.responses)));
if (param.responses.stats_items.length === 1) { if (param.responses.stats_items.length === 1) {
// the responses are equals! // the responses are equals!
response_object.ok = true; response_object.ok = true;
...@@ -394,7 +398,7 @@ ...@@ -394,7 +398,7 @@
if (param.repair === false) { if (param.repair === false) {
// do not repair // do not repair
callback({ callback({
"status": "conflict", "status": 409,
"message": "Some documents are different in the sub storages", "message": "Some documents are different in the sub storages",
"reason": "Storage contents differ" "reason": "Storage contents differ"
}, undefined); }, undefined);
...@@ -460,7 +464,7 @@ ...@@ -460,7 +464,7 @@
/*jslint unparam: true */ /*jslint unparam: true */
if (err) { if (err) {
callback({ callback({
"status": "conflict", "status": 409,
"message": "Unable to retreive attachments", "message": "Unable to retreive attachments",
"reason": err.reason "reason": err.reason
}, undefined); }, undefined);
...@@ -513,7 +517,7 @@ ...@@ -513,7 +517,7 @@
if (new_doc._attachments.hasOwnProperty(i)) { if (new_doc._attachments.hasOwnProperty(i)) {
attachment_to_put.push({ attachment_to_put.push({
"_id": i, "_id": i,
"_mimetype": new_doc._attachments[i].content_type, "_content_type": new_doc._attachments[i].content_type,
"_revs_info": new_doc._revs_info "_revs_info": new_doc._revs_info
}); });
} }
...@@ -555,9 +559,7 @@ ...@@ -555,9 +559,7 @@
var i, attachment; var i, attachment;
if (err) { if (err) {
return callback({ return callback({
"status": 40, "status": 409,
"statusText": "Check Failed",
"error": "check_failed",
"message": "Unable to copy attachments", "message": "Unable to copy attachments",
"reason": err.reason "reason": err.reason
}, undefined); }, undefined);
...@@ -566,7 +568,7 @@ ...@@ -566,7 +568,7 @@
attachment = { attachment = {
"_id": param.doc._id, "_id": param.doc._id,
"_attachment": attachment_to_put[i]._id, "_attachment": attachment_to_put[i]._id,
"_mimetype": attachment_to_put[i]._mimetype, "_content_type": attachment_to_put[i]._content_type,
"_revs_info": attachment_to_put[i]._revs_info, "_revs_info": attachment_to_put[i]._revs_info,
// "_revs_info": param.responses.list[index]._revs_info, // "_revs_info": param.responses.list[index]._revs_info,
"_data": param.responses.attachments[attachment_to_put[i]._id] "_data": param.responses.attachments[attachment_to_put[i]._id]
...@@ -640,7 +642,7 @@ ...@@ -640,7 +642,7 @@
* @param {object} command The JIO command * @param {object} command The JIO command
*/ */
that.post = function (command, metadata, option) { that.post = function (command, metadata, option) {
that.genericRequest(command, "put", metadata, option); that.genericRequest(command, "post", metadata, option);
}; };
/** /**
...@@ -649,7 +651,7 @@ ...@@ -649,7 +651,7 @@
* @param {object} command The JIO command * @param {object} command The JIO command
*/ */
that.put = function (command, metadata, option) { that.put = function (command, metadata, option) {
that.genericRequest(command, "post", metadata, option); that.genericRequest(command, "put", metadata, option);
}; };
/** /**
...@@ -696,7 +698,5 @@ ...@@ -696,7 +698,5 @@
that.removeAttachment = function (command, param, option) { that.removeAttachment = function (command, param, option) {
that.genericRequest(command, "removeAttachment", param, option); that.genericRequest(command, "removeAttachment", param, option);
}; };
return that;
}); });
})); }));
/*jslint indent: 2, maxlen: 80, nomen: true */ /*jslint indent: 2, maxlen: 80, nomen: true */
/*global define, jIO, jio_tests, hex_sha256, test, ok, deepEqual, sinon, /*global define, jIO, test_util, hex_sha256, test, ok, deepEqual, sinon,
expect, module */ expect, module, stop, start, RSVP */
// define([module_name], [dependencies], module); // define([module_name], [dependencies], module);
(function (dependencies, module) { (function (dependencies, module) {
...@@ -8,697 +8,925 @@ ...@@ -8,697 +8,925 @@
if (typeof define === 'function' && define.amd) { if (typeof define === 'function' && define.amd) {
return define(dependencies, module); return define(dependencies, module);
} }
module(jIO, jio_tests, {hex_sha256: hex_sha256}); module(jIO, test_util, {hex_sha256: hex_sha256}, RSVP);
}([ }([
'jio', 'jio',
'jio_tests', 'test_util',
'sha256', 'sha256',
'rsvp',
'localstorage', 'localstorage',
'revisionstorage', 'revisionstorage',
'replicaterevisionstorage' 'replicaterevisionstorage'
], function (jIO, util, sha256) { ], function (jIO, util, sha256, RSVP) {
"use strict"; "use strict";
////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////
// Tools // Tools
/** var tool = {
* Clones all native object in deep. Managed types: Object, Array, String, "deepClone": jIO.util.deepClone,
* Number, Boolean, Function, null. "uniqueJSONStringify": jIO.util.uniqueJSONStringify,
* "readBlobAsBinaryString": jIO.util.readBlobAsBinaryString
* @param {A} object The object to clone };
* @return {A} The cloned object
*/
function deepClone(object) {
var i, cloned;
if (Array.isArray(object)) {
cloned = [];
for (i = 0; i < object.length; i += 1) {
cloned[i] = deepClone(object[i]);
}
return cloned;
}
if (typeof object === "object") {
cloned = {};
for (i in object) {
if (object.hasOwnProperty(i)) {
cloned[i] = deepClone(object[i]);
}
}
return cloned;
}
return object;
}
function generateTools() { function reverse(promise) {
return { return new RSVP.Promise(function (resolve, reject, notify) {
clock: sinon.useFakeTimers(), promise.then(reject, resolve, notify);
spy: util.ospy, }, function () {
tick: util.otick promise.cancel();
}; });
} }
function generateRevisionHash(doc, revisions, deleted_flag) { function generateRevisionHash(doc, revisions, deleted_flag) {
var string; var string;
doc = deepClone(doc); doc = tool.deepClone(doc);
delete doc._rev; delete doc._rev;
delete doc._revs; delete doc._revs;
delete doc._revs_info; delete doc._revs_info;
string = JSON.stringify(doc) + JSON.stringify(revisions) + string = tool.uniqueJSONStringify(doc) +
tool.uniqueJSONStringify(revisions) +
JSON.stringify(deleted_flag ? true : false); JSON.stringify(deleted_flag ? true : false);
return sha256.hex_sha256(string); return sha256.hex_sha256(string);
} }
function unexpectedError(error) {
if (error instanceof Error) {
deepEqual([
error.name + ": " + error.message,
error
], "UNEXPECTED ERROR", "Unexpected error");
} else {
deepEqual(error, "UNEXPECTED ERROR", "Unexpected error");
}
}
////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////
// Tests // Tests
module("Replicate Revision Storage"); module("Replicate Revision Storage");
var testReplicateRevisionStorage = function (sinon, jio_description) { function testReplicateRevisionStorage(jio_description) {
/*jslint unparam: true */
var o = generateTools(), leavesAction, generateLocalPath;
o.jio = jIO.newJio(jio_description);
generateLocalPath = function (storage_description) { var shared = {}, jio, jio_leaves = [];
return "jio/localstorage/" + storage_description.username + "/" + shared.workspace = {};
storage_description.application_name; jio = jIO.createJIO(jio_description, {"workspace": shared.workspace});
};
leavesAction = function (action, storage_description, param) { function leavesAction(action, storage_description) {
var i; var i;
if (param === undefined) { if (storage_description.type === "replicaterevision") {
param = {};
} else {
param = deepClone(param);
}
if (storage_description.storage_list !== undefined) {
// it is the replicate revision storage tree // it is the replicate revision storage tree
for (i = 0; i < storage_description.storage_list.length; i += 1) { for (i = 0; i < storage_description.storage_list.length; i += 1) {
leavesAction(action, storage_description.storage_list[i], param); leavesAction(action, storage_description.storage_list[i]);
} }
} else if (storage_description.sub_storage !== undefined) { } else if (storage_description.type === "revision") {
// it is the revision storage tree // it is the revision storage tree
param.revision = true; leavesAction(action, storage_description.sub_storage);
leavesAction(action, storage_description.sub_storage, param);
} else { } else {
// it is the storage tree leaf // it is the storage tree leaf
param[storage_description.type] = true; action(storage_description);
action(storage_description, param);
} }
}
leavesAction(function (storage_description) {
jio_leaves.push(jIO.createJIO(storage_description, {
"workspace": shared.workspace
}));
}, jio_description);
jio_leaves.run = function (method, argument) {
var i, promises = [];
for (i = 0; i < this.length; i += 1) {
promises[i] = this[i][method].apply(
this[i],
argument
);
}
return RSVP.all(promises);
};
jio_leaves.get = function () {
return this.run("get", arguments);
}; };
o.leavesAction = function (action) { jio_leaves.allDocs = function () {
leavesAction(action, jio_description); return this.run("allDocs", arguments);
}; };
stop();
// post a new document without id // post a new document without id
o.doc = {"title": "post document without id"}; shared.doc = {"title": "post document without id"};
o.spy(o, "status", undefined, "Post document (without id)");
o.jio.post(o.doc, function (err, response) { jio.post(shared.doc).then(function (answer) {
o.f.apply(arguments);
o.response_rev = (err || response).rev; shared.revisions = {"start": 0, "ids": []};
if (util.isUuid((err || response).id)) { shared.uuid = answer.id;
ok(true, "Uuid format"); shared.rev = answer.rev;
o.uuid = (err || response).id; shared.rev_hash = shared.rev.slice(2);
} else { shared.doc._id = shared.uuid;
deepEqual((err || response).id, ok(util.isUuid(shared.uuid), "Uuid should look like " +
"xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx", "Uuid format"); "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx : " + shared.uuid);
} deepEqual(answer, {
}); "id": shared.uuid,
o.tick(o); "method": "post",
"result": "success",
// check document "rev": "1-" + generateRevisionHash(shared.doc, shared.revisions),
o.doc._id = o.uuid; "status": 201,
o.revisions = {"start": 0, "ids": []}; "statusText": "Created"
o.rev_hash = generateRevisionHash(o.doc, o.revisions); }, "Post document (without id)");
o.rev = "1-" + o.rev_hash; delete shared.doc._id;
o.leavesAction(function (storage_description, param) {
var suffix = "", doc = deepClone(o.doc); return jio_leaves.get({"_id": shared.uuid + "." + shared.rev});
if (param.revision) {
deepEqual(o.response_rev, o.rev, "Check revision"); }).then(function (answers) {
doc._id += "." + o.rev; var i;
suffix = "." + o.rev; for (i = 0; i < answers.length; i += 1) {
deepEqual(answers[i].data, {
"_id": shared.uuid + "." + shared.rev,
"title": "post document without id"
}, "Check document " + (i + 1));
} }
deepEqual(util.jsonlocalstorage.getItem(
generateLocalPath(storage_description) + "/" + o.uuid + suffix
), doc, "Check document");
});
// get the post document without revision return jio.get({"_id": shared.uuid}, {
o.spy(o, "value", { "conflicts": true,
"_id": o.uuid, "revs": true,
"title": "post document without id", "revs_info": true
"_rev": o.rev, });
"_revisions": {"start": 1, "ids": [o.rev_hash]},
"_revs_info": [{"rev": o.rev, "status": "available"}] }).then(function (answer) {
}, "Get the generated document, the winner");
o.jio.get({"_id": o.uuid}, { deepEqual(answer.data, {
"conflicts": true, "_id": shared.uuid,
"revs": true, "_rev": shared.rev,
"revs_info": true "_revisions": {
}, o.f); "ids": [shared.rev_hash],
o.tick(o); "start": 1
},
// post a new document with id "_revs_info": [{
o.doc = {"_id": "doc1", "title": "post new doc with id"}; "rev": shared.rev,
o.rev1_1_hash = generateRevisionHash(o.doc, o.revisions); "status": "available"
o.rev1_1 = "1-" + o.rev1_1_hash; }],
o.rev1_1_history = {"start": 1, "ids": [o.rev1_1_hash]}; "title": "post document without id"
o.rev1_1_revs_info = [{"rev": o.rev1_1, "status": "available"}]; }, "Get the generated document, the winner");
o.spy(o, "value", {"ok": true, "id": "doc1", "rev": o.rev1_1},
"Post new document with an id"); // post a new document with id
o.jio.post(o.doc, o.f); shared.doc = {"_id": "doc1", "title": "post new doc with id"};
o.tick(o); shared.rev1_1_hash = generateRevisionHash(shared.doc, shared.revisions);
shared.rev1_1 = "1-" + shared.rev1_1_hash;
// / shared.rev1_1_history = {"start": 1, "ids": [shared.rev1_1_hash]};
// | shared.rev1_1_revs_info = [{"rev": shared.rev1_1, "status": "available"}];
// 1-1
return jio.post(shared.doc);
// check document
o.leavesAction(function (storage_description, param) { }).then(function (answer) {
var suffix = "", doc = deepClone(o.doc);
if (param.revision) { deepEqual(answer, {
doc._id += "." + o.rev1_1; "id": "doc1",
suffix = "." + o.rev1_1; "method": "post",
"result": "success",
"rev": shared.rev1_1,
"status": 201,
"statusText": "Created"
}, "Post new document with an id");
// /
// |
// 1-1
// check document
return jio_leaves.get({"_id": "doc1." + shared.rev1_1});
}).then(function (answers) {
var i;
for (i = 0; i < answers.length; i += 1) {
deepEqual(answers[i].data, {
"_id": "doc1." + shared.rev1_1,
"title": "post new doc with id"
}, "Check document " + (i + 1));
} }
deepEqual(util.jsonlocalstorage.getItem(
generateLocalPath(storage_description) + "/doc1" + suffix
), doc, "Check document");
});
// get the post document without revision // get the post document without revision
o.spy(o, "value", { return jio.get({"_id": "doc1"}, {
"_id": "doc1", "conflicts": true,
"title": "post new doc with id", "revs": true,
"_rev": o.rev1_1, "revs_info": true
"_revisions": {"start": 1, "ids": [o.rev1_1_hash]}, });
"_revs_info": [{"rev": o.rev1_1, "status": "available"}]
}, "Get the previous document (without revision)"); }).then(function (answer) {
o.jio.get({"_id": "doc1"}, {
"conflicts": true, deepEqual(answer.data, {
"revs": true, "_id": "doc1",
"revs_info": true "_rev": shared.rev1_1,
}, o.f); "_revisions": {"start": 1, "ids": [shared.rev1_1_hash]},
o.tick(o); "_revs_info": [{"rev": shared.rev1_1, "status": "available"}],
"title": "post new doc with id"
// post same document without revision }, "Get the previous document (without revision)");
o.doc = {"_id": "doc1", "title": "post same document without revision"};
o.rev1_2_hash = generateRevisionHash(o.doc, o.revisions); // post same document without revision
o.rev1_2 = "1-" + o.rev1_2_hash; shared.doc = {
o.rev1_2_history = {"start": 1, "ids": [o.rev1_2_hash]}; "_id": "doc1",
o.rev1_2_revs_info = [{"rev": o.rev1_2, "status": "available"}]; "title": "post same document without revision"
o.spy(o, "value", {"ok": true, "id": "doc1", "rev": o.rev1_2}, };
"Post same document (without revision)"); shared.rev1_2_hash = generateRevisionHash(shared.doc, shared.revisions);
o.jio.post(o.doc, o.f); shared.rev1_2 = "1-" + shared.rev1_2_hash;
o.tick(o); shared.rev1_2_history = {"start": 1, "ids": [shared.rev1_2_hash]};
shared.rev1_2_revs_info = [{"rev": shared.rev1_2, "status": "available"}];
// /
// / \ return jio.post(shared.doc);
// 1-1 1-2
}).then(function (answer) {
// check document
o.leavesAction(function (storage_description, param) { deepEqual(answer, {
var suffix = "", doc = deepClone(o.doc); "id": "doc1",
if (param.revision) { "method": "post",
doc._id += "." + o.rev1_2; "result": "success",
suffix = "." + o.rev1_2; "rev": shared.rev1_2,
"status": 201,
"statusText": "Created"
}, "Post same document (without revision)");
// /
// / \
// 1-1 1-2
// check document
return jio_leaves.get({"_id": "doc1." + shared.rev1_2});
}).then(function (answers) {
var i;
for (i = 0; i < answers.length; i += 1) {
deepEqual(answers[i].data, {
"_id": "doc1." + shared.rev1_2,
"title": "post same document without revision"
}, "Check document " + (i + 1));
} }
deepEqual(util.jsonlocalstorage.getItem(
generateLocalPath(storage_description) + "/doc1" + suffix
), doc, "Check document");
});
// post a new revision // post a new revision
o.doc = {"_id": "doc1", "title": "post new revision", "_rev": o.rev1_2}; shared.doc = {
o.revisions.start += 1; "_id": "doc1",
o.revisions.ids.unshift(o.rev1_2_hash); "title": "post new revision",
o.rev2_3_hash = generateRevisionHash(o.doc, o.revisions); "_rev": shared.rev1_2
o.rev2_3 = "2-" + o.rev2_3_hash; };
o.rev2_3_history = deepClone(o.rev1_2_history); shared.revisions.start += 1;
o.rev2_3_history.start += 1; shared.revisions.ids.unshift(shared.rev1_2_hash);
o.rev2_3_history.ids.unshift(o.rev2_3_hash); shared.rev2_3_hash = generateRevisionHash(shared.doc, shared.revisions);
o.rev2_3_revs_info = deepClone(o.rev1_2_revs_info); shared.rev2_3 = "2-" + shared.rev2_3_hash;
o.rev2_3_revs_info.unshift({"rev": o.rev2_3, "status": "available"}); shared.rev2_3_history = tool.deepClone(shared.rev1_2_history);
o.spy(o, "value", {"ok": true, "id": "doc1", "rev": o.rev2_3}, shared.rev2_3_history.start += 1;
"Post document (with revision)"); shared.rev2_3_history.ids.unshift(shared.rev2_3_hash);
o.jio.post(o.doc, o.f); shared.rev2_3_revs_info = tool.deepClone(shared.rev1_2_revs_info);
o.tick(o); shared.rev2_3_revs_info.unshift({
"rev": shared.rev2_3,
// / "status": "available"
// / \ });
// 1-1 1-2
// | return jio.post(shared.doc);
// 2-3
}).then(function (answer) {
// check document
o.leavesAction(function (storage_description, param) { deepEqual(answer, {
var suffix = "", doc = deepClone(o.doc); "id": "doc1",
delete doc._rev; "method": "post",
if (param.revision) { "result": "success",
doc._id += "." + o.rev2_3; "rev": shared.rev2_3,
suffix = "." + o.rev2_3; "status": 201,
"statusText": "Created"
}, "Post document (with revision)");
// /
// / \
// 1-1 1-2
// |
// 2-3
// check document
return jio_leaves.get({"_id": "doc1." + shared.rev2_3});
}).then(function (answers) {
var i;
for (i = 0; i < answers.length; i += 1) {
deepEqual(answers[i].data, {
"_id": "doc1." + shared.rev2_3,
"title": "post new revision"
}, "Check document " + (i + 1));
} }
deepEqual(util.jsonlocalstorage.getItem(
generateLocalPath(storage_description) + "/doc1" + suffix
), doc, "Check document");
});
// get the post document with revision // get the post document with revision
o.spy(o, "value", { return jio.get({"_id": "doc1", "_rev": shared.rev1_2}, {
"_id": "doc1", "conflicts": true,
"title": "post same document without revision", "revs": true,
"_rev": o.rev1_2, "revs_info": true
"_revisions": {"start": 1, "ids": [o.rev1_2_hash]}, });
"_revs_info": [{"rev": o.rev1_2, "status": "available"}],
"_conflicts": [o.rev1_1] }).then(function (answer) {
}, "Get the previous document (with revision)");
o.jio.get({"_id": "doc1", "_rev": o.rev1_2}, { deepEqual(answer.data, {
"conflicts": true, "_id": "doc1",
"revs": true, "_rev": shared.rev1_2,
"revs_info": true "_revisions": {"start": 1, "ids": [shared.rev1_2_hash]},
}, o.f); "_revs_info": [{"rev": shared.rev1_2, "status": "available"}],
o.tick(o); "_conflicts": [shared.rev1_1],
"title": "post same document without revision"
// put document without id }, "Get the previous document (with revision)");
o.spy(o, "status", 20, "Put document without id");
o.jio.put({}, o.f); // put document without rev
o.tick(o); shared.doc = {"_id": "doc1", "title": "put new document"};
shared.rev1_4_hash = generateRevisionHash(shared.doc, {
// put document without rev "start": 0,
o.doc = {"_id": "doc1", "title": "put new document"}; "ids": []
o.rev1_4_hash = generateRevisionHash(o.doc, {"start": 0, "ids": []}); });
o.rev1_4 = "1-" + o.rev1_4_hash; shared.rev1_4 = "1-" + shared.rev1_4_hash;
o.rev1_4_history = {"start": 1, "ids": [o.rev1_4_hash]}; shared.rev1_4_history = {"start": 1, "ids": [shared.rev1_4_hash]};
o.rev1_4_revs_info = [{"rev": o.rev1_4, "status": "available"}]; shared.rev1_4_revs_info = [{"rev": shared.rev1_4, "status": "available"}];
o.spy(o, "value", {"id": "doc1", "ok": true, "rev": o.rev1_4},
"Put document without rev"); return jio.put(shared.doc);
o.jio.put(o.doc, o.f);
o.tick(o); }).then(function (answer) {
// __/__ deepEqual(answer, {
// / | \ "id": "doc1",
// 1-1 1-2 1-4 "method": "put",
// | "result": "success",
// 2-3 "rev": shared.rev1_4,
"status": 204,
// put new revision "statusText": "No Content"
o.doc = {"_id": "doc1", "title": "put new revision", "_rev": o.rev1_4}; }, "Put document without rev");
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 = deepClone(o.rev1_4_revs_info); // 1-1 1-2 1-4
o.rev2_5_revs_info.unshift({"rev": o.rev2_5, "status": "available"}); // |
o.spy(o, "value", {"id": "doc1", "ok": true, "rev": o.rev2_5}, // 2-3
"Put new revision");
o.jio.put(o.doc, o.f); // put new revision
o.tick(o); shared.doc = {
"_id": "doc1",
// __/__ "title": "put new revision",
// / | \ "_rev": shared.rev1_4
// 1-1 1-2 1-4 };
// | | shared.rev2_5_hash =
// 2-3 2-5 generateRevisionHash(shared.doc, shared.rev1_4_history);
shared.rev2_5 = "2-" + shared.rev2_5_hash;
// putAttachment to inexistent document shared.rev2_5_history = {
o.doc = { "start": 2,
"_id": "doc2", "ids": [shared.rev2_5_hash, shared.rev1_4_hash]
"_mimetype": "text/plain", };
"_data": "doc 2 - attachment 1", shared.rev2_5_revs_info = tool.deepClone(shared.rev1_4_revs_info);
"_attachment": "attachment1" shared.rev2_5_revs_info.unshift({
}; "rev": shared.rev2_5,
o.rev_hash = generateRevisionHash(o.doc, {"start": 0, "ids": []}); "status": "available"
o.rev = "1-" + o.rev_hash; });
o.spy(o, "value",
{"ok": true, "id": "doc2", "attachment": "attachment1", "rev": o.rev}, return jio.put(shared.doc);
"Put an attachment to an inexistent document");
o.jio.putAttachment(o.doc, o.f); }).then(function (answer) {
o.tick(o);
deepEqual(answer, {
// putAttachment "id": "doc1",
o.doc = { "method": "put",
"_id": "doc1", "result": "success",
"_mimetype": "text/plain", "rev": shared.rev2_5,
"_data": "doc 1 - attachment 1", "status": 204,
"_attachment": "attachment1", "statusText": "No Content"
"_rev": o.rev2_5 }, "Put new revision");
};
o.rev3_6_hash = generateRevisionHash(o.doc, o.rev2_5_history); // __/__
o.rev3_6 = "3-" + o.rev3_6_hash; // / | \
o.rev3_6_history = deepClone(o.rev2_5_history); // 1-1 1-2 1-4
o.rev3_6_history.start += 1; // | |
o.rev3_6_history.ids.unshift(o.rev3_6_hash); // 2-3 2-5
o.rev3_6_revs_info = deepClone(o.rev2_5_revs_info);
o.rev3_6_revs_info.unshift({"rev": o.rev3_6, "status": "available"}); // putAttachment to inexistent document
o.spy(o, "value", { shared.doc = {
"ok": true, "_id": "doc2",
"id": "doc1", "_content_type": "text/plain",
"attachment": "attachment1", "_data": "doc 2 - attachment 1",
"rev": o.rev3_6 "_attachment": "attachment1"
}, "Put an attachment to the first document"); };
o.jio.putAttachment(o.doc, o.f); shared.rev_hash = generateRevisionHash(shared.doc, {
o.tick(o); "start": 0,
"ids": []
// __/__ });
// / | \ shared.rev = "1-" + shared.rev_hash;
// 1-1 1-2 1-4
// | | return jio.putAttachment(shared.doc);
// 2-3 2-5
// | }).then(function (answer) {
// 3-6+a1
deepEqual(answer, {
// get document "attachment": "attachment1",
o.doc = { "id": "doc2",
"_id": "doc1", "method": "putAttachment",
"_rev": o.rev3_6, "result": "success",
"_revisions": o.rev3_6_history, "rev": shared.rev,
"_revs_info": o.rev3_6_revs_info, "status": 204,
"_conflicts": [o.rev2_3, o.rev1_1], "statusText": "No Content"
"_attachments": { }, "Put an attachment to an inexistent document");
"attachment1": {
"length": "doc 1 - attachment 1".length, // putAttachment
"content_type": "text/plain", shared.doc = {
"digest": "md5-0505c1fb6aae02dd1695d33841726564" "_id": "doc1",
} "_content_type": "text/plain",
}, "_data": "doc 1 - attachment 1",
"title": "put new revision" "_attachment": "attachment1",
}; "_rev": shared.rev2_5
o.spy(o, "value", o.doc, "Get document, the winner"); };
o.jio.get({"_id": "doc1"}, { shared.attmt1_digest = "sha256-7b6f6ec759b90a0d2aea0b2a6172544c904c6722" +
"conflicts": true, "1a04fb871477825db92c42ff";
"revs": true, shared.rev3_6_hash =
"revs_info": true generateRevisionHash(shared.doc, shared.rev2_5_history);
}, o.f); shared.rev3_6 = "3-" + shared.rev3_6_hash;
o.tick(o); shared.rev3_6_history = tool.deepClone(shared.rev2_5_history);
shared.rev3_6_history.start += 1;
// get attachment shared.rev3_6_history.ids.unshift(shared.rev3_6_hash);
o.doc = { shared.rev3_6_revs_info = tool.deepClone(shared.rev2_5_revs_info);
"_id": "doc1", shared.rev3_6_revs_info.unshift({
"_attachment": "attachment1" "rev": shared.rev3_6,
}; "status": "available"
o.spy(o, "value", "doc 1 - attachment 1", "Get the winner's attachment"); });
o.jio.getAttachment(o.doc, o.f);
o.tick(o); return jio.putAttachment(shared.doc);
// put document }).then(function (answer) {
o.doc = {
"_id": "doc1", deepEqual(answer, {
"_rev": o.rev3_6, "attachment": "attachment1",
"title": "Put revision, attachment must be copied" "id": "doc1",
}; "method": "putAttachment",
o.rev4_7_hash = generateRevisionHash(o.doc, o.rev3_6_history); "result": "success",
o.rev4_7 = "4-" + o.rev4_7_hash; "rev": shared.rev3_6,
o.rev4_7_history = deepClone(o.rev3_6_history); "status": 204,
o.rev4_7_history.start += 1; "statusText": "No Content"
o.rev4_7_history.ids.unshift(o.rev4_7_hash); }, "Put an attachment to the first document");
o.rev4_7_revs_info = deepClone(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"); // 1-1 1-2 1-4
o.jio.put(o.doc, o.f); // | |
o.tick(o); // 2-3 2-5
// |
// __/__ // 3-6+a1
// / | \
// 1-1 1-2 1-4 // get document
// | | return jio.get({"_id": "doc1"}, {
// 2-3 2-5 "conflicts": true,
// | "revs": true,
// 3-6+a1 "revs_info": true
// | });
// 4-7+a1
}).then(function (answer) {
// get document, attachment must be copied
o.doc = { deepEqual(answer.data, {
"_id": "doc1", "_id": "doc1",
"_rev": o.rev4_7, "_rev": shared.rev3_6,
"title": o.doc.title, "_revisions": shared.rev3_6_history,
"_attachments": { "_revs_info": shared.rev3_6_revs_info,
"attachment1": { "_conflicts": [shared.rev2_3, shared.rev1_1],
"length": "doc 1 - attachment 1".length, "_attachments": {
"content_type": "text/plain", "attachment1": {
"digest": "md5-0505c1fb6aae02dd1695d33841726564" "length": "doc 1 - attachment 1".length,
} "content_type": "text/plain",
}, "digest": shared.attmt1_digest
"_conflicts": [o.rev2_3, o.rev1_1], }
"_revisions": o.rev4_7_history, },
"_revs_info": o.rev4_7_revs_info "title": "put new revision"
}; }, "Get document, the winner");
o.spy(o, "value", o.doc,
"Get the new winner document and its attachment metadata"); // get winner attachment
o.jio.get({"_id": "doc1"}, { return jio.getAttachment({
"conflicts": true, "_id": "doc1",
"revs": true, "_attachment": "attachment1"
"revs_info": true });
}, o.f);
o.tick(o); }).then(function (answer) {
return tool.readBlobAsBinaryString(answer.data);
// get attachment }).then(function (event) {
o.doc = {
"_id": "doc1", deepEqual(event.target.result, "doc 1 - attachment 1",
"_attachment": "attachment1" "Get the winner's attachment");
};
o.spy(o, "value", "doc 1 - attachment 1", // put document
"Get the winner's attachment again"); shared.doc = {
o.jio.getAttachment(o.doc, o.f); "_id": "doc1",
o.tick(o); "_rev": shared.rev3_6,
"title": "Put revision, attachment must be copied"
// remove attachment };
o.doc = { shared.rev4_7_hash =
"_id": "doc1", generateRevisionHash(shared.doc, shared.rev3_6_history);
"_attachment": "attachment1", shared.rev4_7 = "4-" + shared.rev4_7_hash;
"_rev": o.rev4_7 shared.rev4_7_history = tool.deepClone(shared.rev3_6_history);
}; shared.rev4_7_history.start += 1;
o.rev5_8_hash = generateRevisionHash(o.doc, o.rev4_7_history); shared.rev4_7_history.ids.unshift(shared.rev4_7_hash);
o.rev5_8 = "5-" + o.rev5_8_hash; shared.rev4_7_revs_info = tool.deepClone(shared.rev3_6_revs_info);
o.rev5_8_history = deepClone(o.rev4_7_history); shared.rev4_7_revs_info.unshift({
o.rev5_8_history.start += 1; "rev": shared.rev4_7,
o.rev5_8_history.ids.unshift(o.rev5_8_hash); "status": "available"
o.rev5_8_revs_info = deepClone(o.rev4_7_revs_info); });
o.rev5_8_revs_info.unshift({"rev": o.rev5_8, "status": "available"});
o.spy(o, "value", { return jio.put(shared.doc);
"ok": true,
"id": "doc1", }).then(function (answer) {
"attachment": "attachment1",
"rev": o.rev5_8 deepEqual(answer, {
}, "Remove attachment"); "id": "doc1",
o.jio.removeAttachment(o.doc, o.f); "method": "put",
o.tick(o); "result": "success",
"rev": shared.rev4_7,
"status": 204,
// __/__ "statusText": "No Content"
// / | \ }, "Update document, attachment should be copied");
// 1-1 1-2 1-4
// | | // __/__
// 2-3 2-5 // / | \
// | // 1-1 1-2 1-4
// 3-6+a1 // | |
// | // 2-3 2-5
// 4-7+a1 // |
// | // 3-6+a1
// 5-8 // |
// 4-7+a1
// get document to check attachment existence
o.doc = { // get document, attachment must be copied
"_id": "doc1", shared.doc = {
"_rev": o.rev5_8, "_id": "doc1",
"title": "Put revision, attachment must be copied", "_rev": shared.rev4_7,
"_conflicts": [o.rev2_3, o.rev1_1], "title": shared.doc.title,
"_revisions": o.rev5_8_history, "_attachments": {
"_revs_info": o.rev5_8_revs_info "attachment1": {
}; "length": "doc 1 - attachment 1".length,
o.spy(o, "value", o.doc, "content_type": "text/plain",
"Get the new winner document, no attachment must be provided"); "digest": shared.attmt1_digest
o.jio.get({"_id": "doc1"}, { }
"conflicts": true, },
"revs": true, "_conflicts": [shared.rev2_3, shared.rev1_1],
"revs_info": true "_revisions": shared.rev4_7_history,
}, o.f); "_revs_info": shared.rev4_7_revs_info
o.tick(o); };
// get specific document return jio.get({"_id": "doc1"}, {
o.doc = { "conflicts": true,
"_id": "doc1", "revs": true,
"_rev": o.rev4_7, "revs_info": true
"title": o.doc.title, });
"_attachments": {
"attachment1": { }).then(function (answer) {
"length": "doc 1 - attachment 1".length,
"content_type": "text/plain", deepEqual(answer.data, shared.doc,
"digest": "md5-0505c1fb6aae02dd1695d33841726564" "Get the new winner document and its attachment metadata");
}
}, // get winner attachment
"_conflicts": [o.rev2_3, o.rev1_1], return jio.getAttachment({
"_revisions": o.rev4_7_history, "_id": "doc1",
"_revs_info": o.rev4_7_revs_info "_attachment": "attachment1"
}; });
o.spy(o, "value", o.doc,
"Get the new winner document and its attachment metadata"); }).then(function (answer) {
o.jio.get({"_id": "doc1", "_rev": o.rev4_7}, { return tool.readBlobAsBinaryString(answer.data);
"conflicts": true, }).then(function (event) {
"revs": true,
"revs_info": true deepEqual(event.target.result, "doc 1 - attachment 1",
}, o.f); "Get the winner's attachment again");
o.tick(o);
// remove attachment
// get inexistent attachment shared.doc = {
o.spy(o, "status", 404, "Get inexistent winner attachment" + "_id": "doc1",
" -> 404 Not Found"); "_attachment": "attachment1",
o.jio.get({"_id": "doc1/attachment1"}, o.f); "_rev": shared.rev4_7
o.tick(o); };
shared.rev5_8_hash =
// get specific attachment generateRevisionHash(shared.doc, shared.rev4_7_history);
o.doc = { shared.rev5_8 = "5-" + shared.rev5_8_hash;
"_id": "doc1", shared.rev5_8_history = tool.deepClone(shared.rev4_7_history);
"_attachment": "attachment1", shared.rev5_8_history.start += 1;
"_rev": o.rev3_6 shared.rev5_8_history.ids.unshift(shared.rev5_8_hash);
}; shared.rev5_8_revs_info = tool.deepClone(shared.rev4_7_revs_info);
o.spy(o, "value", "doc 1 - attachment 1", "Get a specific attachment"); shared.rev5_8_revs_info.unshift({
o.jio.getAttachment(o.doc, o.f); "rev": shared.rev5_8,
o.tick(o); "status": "available"
});
// remove specific document and conflict
o.doc = {"_id": "doc1", "_rev": o.rev1_1}; return jio.removeAttachment(shared.doc);
// generate with deleted_flag
o.rev2_9_hash = generateRevisionHash(o.doc, o.rev1_1_history, true); }).then(function (answer) {
o.rev2_9 = "2-" + o.rev2_9_hash;
o.rev2_9_history = deepClone(o.rev1_1_history); deepEqual(answer, {
o.rev2_9_history.start += 1; "attachment": "attachment1",
o.rev2_9_history.ids.unshift(o.rev2_9_hash); "id": "doc1",
o.rev2_9_revs_info = deepClone(o.rev1_1_revs_info); "method": "removeAttachment",
o.rev2_9_revs_info.unshift({"rev": o.rev2_9, "status": "deleted"}); "result": "success",
o.spy(o, "value", {"ok": true, "id": "doc1", "rev": o.rev2_9}, "rev": shared.rev5_8,
"Remove specific document, and one conflict"); "status": 204,
o.jio.remove(o.doc, o.f); "statusText": "No Content"
o.tick(o); }, "Remove attachment");
// __/___ // __/__
// / | \ // / | \
// 1-1 1-2 1-4 // 1-1 1-2 1-4
// | | | // | |
// D2-9 2-3 2-5 // 2-3 2-5
// | // |
// 3-6+a1 // 3-6+a1
// | // |
// 4-7+a1 // 4-7+a1
// | // |
// 5-8 // 5-8
// remove specific document and conflict // get document to check attachment existence
o.doc = {"_id": "doc1", "_rev": o.rev2_3}; shared.doc = {
o.rev3_10_hash = generateRevisionHash(o.doc, o.rev2_3_history, true); "_id": "doc1",
o.rev3_10 = "3-" + o.rev3_10_hash; "_rev": shared.rev5_8,
o.rev3_10_history = deepClone(o.rev2_3_history); "title": "Put revision, attachment must be copied",
o.rev3_10_history.start += 1; "_conflicts": [shared.rev2_3, shared.rev1_1],
o.rev3_10_history.ids.unshift(o.rev3_10_hash); "_revisions": shared.rev5_8_history,
o.rev3_10_revs_info = deepClone(o.rev2_3_revs_info); "_revs_info": shared.rev5_8_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"); return jio.get({"_id": "doc1"}, {
o.jio.remove(o.doc, o.f); "conflicts": true,
o.tick(o); "revs": true,
"revs_info": true
// ___/____ });
// / | \
// 1-1 1-2 1-4 }).then(function (answer) {
// | | |
// D2-9 2-3 2-5 deepEqual(answer.data, shared.doc,
// | | "Get the new winner document, no attachment must be provided");
// D3-10 3-6+a1
// | // get specific document
// 4-7+a1 shared.doc = {
// | "_id": "doc1",
// 5-8 "_rev": shared.rev4_7,
"title": shared.doc.title,
// get document no more conflict "_attachments": {
o.doc = { "attachment1": {
"_id": "doc1", "length": "doc 1 - attachment 1".length,
"_rev": o.rev5_8, "content_type": "text/plain",
"title": "Put revision, attachment must be copied", "digest": shared.attmt1_digest
"_revisions": o.rev5_8_history, }
"_revs_info": o.rev5_8_revs_info },
}; "_conflicts": [shared.rev2_3, shared.rev1_1],
o.spy(o, "value", o.doc, "_revisions": shared.rev4_7_history,
"Get the new winner document, no more conflicts"); "_revs_info": shared.rev4_7_revs_info
o.jio.get({"_id": "doc1"}, { };
"conflicts": true,
"revs": true, return jio.get({"_id": "doc1", "_rev": shared.rev4_7}, {
"revs_info": true "conflicts": true,
}, o.f); "revs": true,
o.tick(o); "revs_info": true
});
// remove document
o.doc = { }).then(function (answer) {
"_id": "doc1",
"_rev": o.rev5_8 deepEqual(answer.data, shared.doc,
}; "Get specific revision and its attachment metadata");
o.rev6_11_hash = generateRevisionHash(o.doc, o.rev5_8_history, true);
o.rev6_11 = "6-" + o.rev6_11_hash; // get inexistent attachment
o.spy(o, "value", {"ok": true, "id": "doc1", "rev": o.rev6_11}, return reverse(jio.getAttachment({
"Remove the last document"); "_id": "doc1",
o.jio.remove(o.doc, o.f); "_attachment": "attachment1"
o.tick(o); }));
// ___/____ }).then(function (answer) {
// / | \
// 1-1 1-2 1-4 deepEqual(answer, {
// | | | "attachment": "attachment1",
// D2-9 2-3 2-5 "error": "not_found",
// | | "id": "doc1",
// D3-10 3-6+a1 "message": "Unable to get an inexistent attachment",
// | "method": "getAttachment",
// 4-7+a1 "reason": "missing",
// | "result": "error",
// 5-8 "status": 404,
// | "statusText": "Not Found"
// D6-11 }, "Get inexistent winner attachment -> 404 Not Found");
// get inexistent document // get specific attachment
o.spy(o, "status", 404, "Get inexistent document -> 404 Not Found"); shared.doc = {
o.jio.get({"_id": "doc3"}, { "_id": "doc1",
"conflicts": true, "_attachment": "attachment1",
"revs": true, "_rev": shared.rev3_6
"revisions": true };
}, o.f); return jio.getAttachment(shared.doc);
o.tick(o);
}).then(function (answer) {
// get specific deleted document return tool.readBlobAsBinaryString(answer.data);
o.spy(o, "status", 404, "Get deleted document -> 404 Not Found"); }).then(function (event) {
o.jio.get({"_id": "doc1", "rev": o.rev3_10}, {
"conflicts": true, deepEqual(event.target.result, "doc 1 - attachment 1",
"revs": true, "Get a specific attachment");
"revs_info": true
}, o.f); // remove specific document and conflict
o.tick(o); shared.doc = {"_id": "doc1", "_rev": shared.rev1_1};
// generate with deleted_flag
// get specific deleted document shared.rev2_9_hash =
o.spy(o, "status", 404, "Get deleted document -> 404 Not Found"); generateRevisionHash(shared.doc, shared.rev1_1_history, true);
o.jio.get({"_id": "doc1"}, { shared.rev2_9 = "2-" + shared.rev2_9_hash;
"conflicts": true, shared.rev2_9_history = tool.deepClone(shared.rev1_1_history);
"revs": true, shared.rev2_9_history.start += 1;
"revs_info": true shared.rev2_9_history.ids.unshift(shared.rev2_9_hash);
}, o.f); shared.rev2_9_revs_info = tool.deepClone(shared.rev1_1_revs_info);
o.tick(o); shared.rev2_9_revs_info.unshift({
"rev": shared.rev2_9,
util.closeAndcleanUpJio(o.jio); "status": "deleted"
});
return jio.remove(shared.doc);
}).then(function (answer) {
deepEqual(answer, {
"id": "doc1",
"method": "remove",
"result": "success",
"rev": shared.rev2_9,
"status": 204,
"statusText": "No Content"
}, "Remove specific document, and one conflict");
// __/___
// / | \
// 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
shared.doc = {"_id": "doc1", "_rev": shared.rev2_3};
shared.rev3_10_hash =
generateRevisionHash(shared.doc, shared.rev2_3_history, true);
shared.rev3_10 = "3-" + shared.rev3_10_hash;
shared.rev3_10_history = tool.deepClone(shared.rev2_3_history);
shared.rev3_10_history.start += 1;
shared.rev3_10_history.ids.unshift(shared.rev3_10_hash);
shared.rev3_10_revs_info = tool.deepClone(shared.rev2_3_revs_info);
shared.rev3_10_revs_info.unshift({
"rev": shared.rev3_10,
"status": "deleted"
});
return jio.remove(shared.doc);
}).then(function (answer) {
deepEqual(answer, {
"id": "doc1",
"method": "remove",
"result": "success",
"rev": shared.rev3_10,
"status": 204,
"statusText": "No Content"
}, "Remove anther specific document, and one conflict");
// ___/____
// / | \
// 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
shared.doc = {
"_id": "doc1",
"_rev": shared.rev5_8,
"title": "Put revision, attachment must be copied",
"_revisions": shared.rev5_8_history,
"_revs_info": shared.rev5_8_revs_info
};
return jio.get({"_id": "doc1"}, {
"conflicts": true,
"revs": true,
"revs_info": true
});
}).then(function (answer) {
deepEqual(answer.data, shared.doc,
"Get the new winner document, no more conflicts");
// remove document
shared.doc = {
"_id": "doc1",
"_rev": shared.rev5_8
};
shared.rev6_11_hash =
generateRevisionHash(shared.doc, shared.rev5_8_history, true);
shared.rev6_11 = "6-" + shared.rev6_11_hash;
return jio.remove(shared.doc);
}).then(function (answer) {
deepEqual(answer, {
"id": "doc1",
"method": "remove",
"result": "success",
"rev": shared.rev6_11,
"status": 204,
"statusText": "No Content"
}, "Remove the last document");
// ___/____
// / | \
// 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
return reverse(jio.get({"_id": "doc3"}, {
"conflicts": true,
"revs": true,
"revisions": true
}));
}).then(function (answer) {
deepEqual(answer, {
"error": "not_found",
"id": "doc3",
"message": "Document not found",
"method": "get",
"reason": "missing",
"result": "error",
"status": 404,
"statusText": "Not Found"
}, "Get inexistent document -> 404 Not Found");
// get specific deleted document
return reverse(jio.get({"_id": "doc1", "rev": shared.rev3_10}, {
"conflicts": true,
"revs": true,
"revs_info": true
}));
}).then(function (answer) {
deepEqual(answer, {
"error": "not_found",
"id": "doc1",
"message": "Document not found",
"method": "get",
"reason": "deleted",
"result": "error",
"status": 404,
"statusText": "Not Found"
}, "Get specific deleted document -> 404 Not Found");
// get deleted document
return reverse(jio.get({"_id": "doc1"}, {
"conflicts": true,
"revs": true,
"revs_info": true
}));
}).then(function (answer) {
deepEqual(answer, {
"error": "not_found",
"id": "doc1",
"message": "Document not found",
"method": "get",
"reason": "deleted",
"result": "error",
"status": 404,
"statusText": "Not Found"
}, "Get deleted document -> 404 Not Found");
}).fail(unexpectedError).always(start);
}; }
test("[Revision + Local Storage] Scenario", function () { test("[Revision + Local Storage] Scenario", function () {
testReplicateRevisionStorage(this, { testReplicateRevisionStorage({
"type": "replicaterevision", "type": "replicaterevision",
"storage_list": [{ "storage_list": [{
"type": "revision", "type": "revision",
"sub_storage": { "sub_storage": {
"type": "local", "type": "local",
"username": "ureprevloc", "username": "ureprevloc",
"application_name": "areprevloc" "mode": "memory"
} }
}] }]
}); });
}); });
test("[Replicate Revision + Revision + Local Storage] Scenario", function () { test("[Replicate Revision + Revision + Local Storage] Scenario", function () {
testReplicateRevisionStorage(this, { testReplicateRevisionStorage({
"type": "replicaterevision", "type": "replicaterevision",
"storage_list": [{ "storage_list": [{
"type": "replicaterevision", "type": "replicaterevision",
...@@ -707,34 +935,34 @@ ...@@ -707,34 +935,34 @@
"sub_storage": { "sub_storage": {
"type": "local", "type": "local",
"username": "urepreprevloc", "username": "urepreprevloc",
"application_name": "arepreprevloc" "mode": "memory"
} }
}] }]
}] }]
}); });
}); });
test("2x [Revision + Local Storage] Scenario", function () { test("2x [Revision + Local Storage] Scenario", function () {
testReplicateRevisionStorage(this, { testReplicateRevisionStorage({
"type": "replicaterevision", "type": "replicaterevision",
"storage_list": [{ "storage_list": [{
"type": "revision", "type": "revision",
"sub_storage": { "sub_storage": {
"type": "local", "type": "local",
"username": "ureprevlocloc1", "username": "ureprevlocloc1",
"application_name": "areprevlocloc1" "mode": "memory"
} }
}, { }, {
"type": "revision", "type": "revision",
"sub_storage": { "sub_storage": {
"type": "local", "type": "local",
"username": "ureprevlocloc2", "username": "ureprevlocloc2",
"application_name": "areprevlocloc2" "mode": "memory"
} }
}] }]
}); });
}); });
test("2x [Replicate Rev + 2x [Rev + Local]] Scenario", function () { test("2x [Replicate Rev + 2x [Rev + Local]] Scenario", function () {
testReplicateRevisionStorage(this, { testReplicateRevisionStorage({
"type": "replicaterevision", "type": "replicaterevision",
"storage_list": [{ "storage_list": [{
"type": "replicaterevision", "type": "replicaterevision",
...@@ -743,14 +971,14 @@ ...@@ -743,14 +971,14 @@
"sub_storage": { "sub_storage": {
"type": "local", "type": "local",
"username": "urepreprevloc1", "username": "urepreprevloc1",
"application_name": "arepreprevloc1" "mode": "memory"
} }
}, { }, {
"type": "revision", "type": "revision",
"sub_storage": { "sub_storage": {
"type": "local", "type": "local",
"username": "urepreprevloc2", "username": "urepreprevloc2",
"application_name": "arepreprevloc2" "mode": "memory"
} }
}] }]
}, { }, {
...@@ -760,253 +988,336 @@ ...@@ -760,253 +988,336 @@
"sub_storage": { "sub_storage": {
"type": "local", "type": "local",
"username": "urepreprevloc3", "username": "urepreprevloc3",
"application_name": "arepreprevloc3" "mode": "memory"
} }
}, { }, {
"type": "revision", "type": "revision",
"sub_storage": { "sub_storage": {
"type": "local", "type": "local",
"username": "urepreprevloc4", "username": "urepreprevloc4",
"application_name": "arepreprevloc4" "mode": "memory"
} }
}] }]
}] }]
}); });
}); });
function replicateStorageSynchronisationGenerator( function replicateStorageSynchronisationGenerator(jio_description) {
that,
description, var shared = {}, jio, jio_leaves = [];
index shared.workspace = {};
) { jio = jIO.createJIO(jio_description, {"workspace": shared.workspace});
/*jslint unparam: true */
var o = generateTools(); function leavesAction(action, storage_description) {
var i;
if (storage_description.type === "replicaterevision") {
// 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]);
}
} else if (storage_description.type === "revision") {
// it is the revision storage tree
leavesAction(action, storage_description.sub_storage);
} else {
// it is the storage tree leaf
action(storage_description);
}
}
leavesAction(function (storage_description) {
jio_leaves.push(jIO.createJIO(storage_description, {
"workspace": shared.workspace
}));
}, jio_description);
if (jio_leaves.length !== 4) {
// please make a jio description with 4 localstorage
ok(false, "More or less then 4 localstorage were provided");
return;
}
jio_leaves.run = function (method, argument) {
var i, promises = [];
for (i = 0; i < this.length; i += 1) {
promises[i] = this[i][method].apply(
this[i],
argument
);
}
return RSVP.all(promises);
};
jio_leaves.get = function () {
return this.run("get", arguments);
};
jio_leaves.put = function () {
return this.run("put", arguments);
};
jio_leaves.allDocs = function () {
return this.run("allDocs", arguments);
};
o.jio = jIO.newJio(description); stop();
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 // add documents to localstorage
o.doctree1_1 = { shared.doctree1_1 = {
"_id": "doc1.revision_tree.json", "_id": "doc1.revision_tree.json",
"children": [{ "children": JSON.stringify([{
"rev": "1-111", "rev": "1-111",
"status": "available", "status": "available",
"children": [] "children": []
}] }])
}; };
o.doc1_1 = {"_id": "doc1.1-111", "title": "A"}; shared.doc1_1 = {"_id": "doc1.1-111", "title": "A"};
util.jsonlocalstorage.setItem(o.localpath1 + "/doc1.revision_tree.json", jio_leaves.put(shared.doctree1_1).then(function () {
o.doctree1_1); return jio_leaves.put(shared.doc1_1);
util.jsonlocalstorage.setItem(o.localpath2 + "/doc1.revision_tree.json", }).then(function () {
o.doctree1_1);
util.jsonlocalstorage.setItem(o.localpath3 + "/doc1.revision_tree.json",
o.doctree1_1);
util.jsonlocalstorage.setItem(o.localpath4 + "/doc1.revision_tree.json",
o.doctree1_1);
util.jsonlocalstorage.setItem(o.localpath1 + "/" + o.doc1_1._id, o.doc1_1);
util.jsonlocalstorage.setItem(o.localpath2 + "/" + o.doc1_1._id, o.doc1_1);
util.jsonlocalstorage.setItem(o.localpath3 + "/" + o.doc1_1._id, o.doc1_1);
util.jsonlocalstorage.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(
util.jsonlocalstorage.getItem(o.localpath1 + "/doc1.revision_tree.json"),
o.doctree1_1,
"Check revision tree 1, no synchro done"
);
deepEqual(
util.jsonlocalstorage.getItem(o.localpath2 + "/doc1.revision_tree.json"),
o.doctree1_1,
"Check revision tree 2, no synchro done"
);
deepEqual(
util.jsonlocalstorage.getItem(o.localpath3 + "/doc1.revision_tree.json"),
o.doctree1_1,
"Check revision tree 3, no synchro done"
);
deepEqual(
util.jsonlocalstorage.getItem(o.localpath4 + "/doc1.revision_tree.json"),
o.doctree1_1,
"Check revision tree 4, no synchro done"
);
// add documents to localstorage // no synchronisation
o.doctree2_2 = deepClone(o.doctree1_1); return jio.check({"_id": "doc1"});
o.doctree2_2.children[0].children.push({
"rev": "2-222", }).then(function (answer) {
"status": "available",
"children": [] deepEqual(answer, {
}); "id": "doc1",
o.doc2_2 = { "method": "check",
"_id": "doc1.2-222", "result": "success",
"title": "B", "status": 204,
"_attachments": { "statusText": "No Content"
"haha": { }, "Check document");
"length": 3,
"digest": "md5-900150983cd24fb0d6963f7d28e17f72", return jio.check({"_id": "doc1", "_rev": "1-111"});
"content_type": "text/plain"
}).then(function (answer) {
deepEqual(answer, {
"id": "doc1",
"method": "check",
"result": "success",
"status": 204,
"statusText": "No Content"
}, "Check document with revision");
return jio.repair({"_id": "doc1"});
}).then(function (answer) {
deepEqual(answer, {
"id": "doc1",
"method": "repair",
"result": "success",
"status": 204,
"statusText": "No Content"
}, "Repair document");
// check documents from localstorage
return jio_leaves.get({"_id": "doc1.revision_tree.json"});
}).then(function (answers) {
var i;
for (i = 0; i < answers.length; i += 1) {
deepEqual(answers[i].data, shared.doctree1_1, "Check revision tree " +
i + ", no syncho done");
}
// add documents to localstorage
shared.doctree2_2 = tool.deepClone(shared.doctree1_1);
shared.doctree2_2.children = JSON.parse(shared.doctree2_2.children);
shared.doctree2_2.children[0].children.push({
"rev": "2-222",
"status": "available",
"children": []
});
shared.doctree2_2.children = JSON.stringify(shared.doctree2_2.children);
shared.doc2_2 = {
"_id": "doc1.2-222",
"title": "B",
"_attachments": {
"haha": {
"length": 3,
"digest": "sha256-ba7816bf8f01cfea414140de5dae2223b00361a3" +
"96177a9cb410ff61f20015ad",
"content_type": "text/plain"
}
} }
};
return jio_leaves[0].put(shared.doctree2_2);
}).then(function () {
return jio_leaves[0].put(shared.doc2_2);
}).then(function () {
return jio_leaves[0].putAttachment({
"_id": shared.doc2_2._id,
"_attachment": "haha",
"_data": "abc",
"_content_type": "text/plain"
});
}).then(function () {
// document synchronisation without conflict
return reverse(jio.check({"_id": "doc1"}));
}).then(function (answer) {
deepEqual(answer, {
"error": "conflict",
"id": "doc1",
"message": "Some documents are different in the sub storages",
"method": "check",
"reason": "Storage contents differ",
"result": "error",
"status": 409,
"statusText": "Conflict"
}, "Check document");
return jio.repair({"_id": "doc1"});
}).then(function (answer) {
deepEqual(answer, {
"id": "doc1",
"method": "repair",
"result": "success",
"status": 204,
"statusText": "No Content"
}, "Repair document");
// check document trees
return jio_leaves.get({"_id": "doc1.revision_tree.json"});
}).then(function (answers) {
var i;
for (i = 0; i < answers.length; i += 1) {
deepEqual(answers[i].data, shared.doctree2_2, "Check revision tree " +
i + ", " + (i ? "" : "no") + " synchro done");
} }
};
util.jsonlocalstorage.setItem(o.localpath1 + "/doc1.revision_tree.json",
o.doctree2_2);
util.jsonlocalstorage.setItem(o.localpath1 + "/" + o.doc2_2._id, o.doc2_2);
util.jsonlocalstorage.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(
util.jsonlocalstorage.getItem(o.localpath1 + "/doc1.revision_tree.json"),
o.doctree2_2,
"Check revision tree 1, no synchro done"
);
deepEqual(
util.jsonlocalstorage.getItem(o.localpath2 + "/doc1.revision_tree.json"),
o.doctree2_2,
"Check revision tree 2, revision synchro done"
);
deepEqual(
util.jsonlocalstorage.getItem(o.localpath3 + "/doc1.revision_tree.json"),
o.doctree2_2,
"Check revision tree 3, revision synchro done"
);
deepEqual(
util.jsonlocalstorage.getItem(o.localpath3 + "/doc1.2-222"),
o.doc2_2,
"Check document 3"
);
deepEqual(
util.jsonlocalstorage.getItem(o.localpath3 + "/doc1.2-222/haha"),
"abc",
"Check attachment 3"
);
deepEqual(
util.jsonlocalstorage.getItem(o.localpath4 + "/doc1.revision_tree.json"),
o.doctree2_2,
"Check revision tree 4, revision synchro done"
);
// add documents to localstorage // check document 2
o.doctree2_3 = deepClone(o.doctree2_2); return jio_leaves[2].get({"_id": "doc1.2-222"});
o.doctree2_3.children[0].children.unshift({
"rev": "2-223", }).then(function (answer) {
"status": "available",
"children": [] deepEqual(answer.data, shared.doc2_2, "Check document 2");
});
o.doc2_3 = {"_id": "doc1.2-223", "title": "C"}; // check attachment 2
util.jsonlocalstorage.setItem(o.localpath1 + "/doc1.revision_tree.json", return jio_leaves[2].getAttachment({
o.doctree2_3); "_id": "doc1.2-222",
util.jsonlocalstorage.setItem(o.localpath1 + "/" + o.doc2_3._id, o.doc2_3); "_attachment": "haha"
});
// document synchronisation with conflict
o.spy(o, "status", 41, "Check document"); }).then(function (answer) {
o.jio.check({"_id": "doc1"}, o.f); return tool.readBlobAsBinaryString(answer.data);
o.tick(o, 50000); }).then(function (event) {
o.spy(o, "value", {"ok": true, "id": "doc1"}, deepEqual(event.target.result, "abc", "Check attachment 2");
"Repair document");
o.jio.repair({"_id": "doc1"}, o.f); // add documents to localstorage
o.tick(o, 50000); shared.doctree2_3 = tool.deepClone(shared.doctree2_2);
shared.doctree2_3.children = JSON.parse(shared.doctree2_3.children);
// check documents from localstorage shared.doctree2_3.children[0].children.unshift({
deepEqual( "rev": "2-223",
util.jsonlocalstorage.getItem(o.localpath1 + "/doc1.revision_tree.json"), "status": "available",
o.doctree2_3, "children": []
"Check revision tree 1, rev synchro" });
); shared.doctree2_3.children = JSON.stringify(shared.doctree2_3.children);
deepEqual( shared.doc2_3 = {"_id": "doc1.2-223", "title": "C"};
util.jsonlocalstorage.getItem(o.localpath2 + "/doc1.revision_tree.json"),
o.doctree2_3, return jio_leaves[0].put(shared.doctree2_3);
"Check revision tree 2, rev synchro" }).then(function () {
); return jio_leaves[0].put(shared.doc2_3);
deepEqual( }).then(function () {
util.jsonlocalstorage.getItem(o.localpath3 + "/doc1.revision_tree.json"),
o.doctree2_3, // document synchronisation with conflict
"Check revision tree 3, rev synchro" return reverse(jio.check({"_id": "doc1"}));
);
deepEqual( }).then(function (answer) {
util.jsonlocalstorage.getItem(o.localpath3 + "/doc1.2-223"),
o.doc2_3, deepEqual(answer, {
"Check document 3" "error": "conflict",
); "id": "doc1",
deepEqual( "message": "Some documents are different in the sub storages",
util.jsonlocalstorage.getItem(o.localpath4 + "/doc1.revision_tree.json"), "method": "check",
o.doctree2_3, "reason": "Storage contents differ",
"Check revision tree 4, rev synchro" "result": "error",
); "status": 409,
"statusText": "Conflict"
util.closeAndcleanUpJio(o.jio); }, "Check document");
return jio.repair({"_id": "doc1"});
}).then(function (answer) {
deepEqual(answer, {
"id": "doc1",
"method": "repair",
"result": "success",
"status": 204,
"statusText": "No Content"
}, "Repair document");
// check documents from localstorage
return jio_leaves.get({"_id": "doc1.revision_tree.json"});
}).then(function (answers) {
var i;
for (i = 0; i < answers.length; i += 1) {
deepEqual(answers[i].data, shared.doctree2_3, "Check revision tree " +
i + ", " + (i ? "" : "no") + " synchro done");
}
// check document 2
return jio_leaves[2].get({"_id": "doc1.2-223"});
}).then(function (answer) {
deepEqual(answer.data, shared.doc2_3, "Check document 2");
}).fail(unexpectedError).always(start);
} }
test("Storage Synchronisation (Repair) 4x [Rev + Local]", function () { test("Storage Synchronisation (Repair) 4x [Rev + Local]", function () {
replicateStorageSynchronisationGenerator(this, { replicateStorageSynchronisationGenerator({
"type": "replicaterevision", "type": "replicaterevision",
"storage_list": [{ "storage_list": [{
"type": "revision", "type": "revision",
"sub_storage": { "sub_storage": {
"type": "local", "type": "local",
"username": "usyncreprevlocloc1", "username": "usyncreprevlocloc1",
"application_name": "1" "application_name": "1",
"mode": "memory"
} }
}, { }, {
"type": "revision", "type": "revision",
"sub_storage": { "sub_storage": {
"type": "local", "type": "local",
"username": "usyncreprevlocloc2", "username": "usyncreprevlocloc2",
"application_name": "1" "application_name": "1",
"mode": "memory"
} }
}, { }, {
"type": "revision", "type": "revision",
"sub_storage": { "sub_storage": {
"type": "local", "type": "local",
"username": "usyncreprevlocloc3", "username": "usyncreprevlocloc3",
"application_name": "1" "application_name": "1",
"mode": "memory"
} }
}, { }, {
"type": "revision", "type": "revision",
"sub_storage": { "sub_storage": {
"type": "local", "type": "local",
"username": "usyncreprevlocloc4", "username": "usyncreprevlocloc4",
"application_name": "1" "application_name": "1",
"mode": "memory"
} }
}] }]
}, "1"); });
}); });
test( test(
"Storage Synchronisation (Repair) 2x [Rep 2x [Rev + Local]]", "Storage Synchronisation (Repair) 2x [Rep 2x [Rev + Local]]",
function () { function () {
replicateStorageSynchronisationGenerator(this, { replicateStorageSynchronisationGenerator({
"type": "replicaterevision", "type": "replicaterevision",
"storage_list": [{ "storage_list": [{
"type": "replicaterevision", "type": "replicaterevision",
...@@ -1015,14 +1326,16 @@ ...@@ -1015,14 +1326,16 @@
"sub_storage": { "sub_storage": {
"type": "local", "type": "local",
"username": "usyncreprevlocloc1", "username": "usyncreprevlocloc1",
"application_name": "2" "application_name": "2",
"mode": "memory"
} }
}, { }, {
"type": "revision", "type": "revision",
"sub_storage": { "sub_storage": {
"type": "local", "type": "local",
"username": "usyncreprevlocloc2", "username": "usyncreprevlocloc2",
"application_name": "2" "application_name": "2",
"mode": "memory"
} }
}] }]
}, { }, {
...@@ -1032,18 +1345,20 @@ ...@@ -1032,18 +1345,20 @@
"sub_storage": { "sub_storage": {
"type": "local", "type": "local",
"username": "usyncreprevlocloc3", "username": "usyncreprevlocloc3",
"application_name": "2" "application_name": "2",
"mode": "memory"
} }
}, { }, {
"type": "revision", "type": "revision",
"sub_storage": { "sub_storage": {
"type": "local", "type": "local",
"username": "usyncreprevlocloc4", "username": "usyncreprevlocloc4",
"application_name": "2" "application_name": "2",
"mode": "memory"
} }
}] }]
}] }]
}, "2"); });
} }
); );
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment