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,6 +257,16 @@ ...@@ -264,6 +257,16 @@
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
// // 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 () { // functions.repairAllSubStorages = function () {
var i; var i;
...@@ -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,242 +8,288 @@ ...@@ -8,242 +8,288 @@
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() {
return {
clock: sinon.useFakeTimers(),
spy: util.ospy,
tick: util.otick
}; };
function reverse(promise) {
return new RSVP.Promise(function (resolve, reject, notify) {
promise.then(reject, resolve, notify);
}, function () {
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; var shared = {}, jio, jio_leaves = [];
shared.workspace = {};
jio = jIO.createJIO(jio_description, {"workspace": shared.workspace});
o.jio = jIO.newJio(jio_description); function leavesAction(action, storage_description) {
generateLocalPath = function (storage_description) {
return "jio/localstorage/" + storage_description.username + "/" +
storage_description.application_name;
};
leavesAction = function (action, storage_description, param) {
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);
}; };
o.leavesAction = function (action) { jio_leaves.get = function () {
leavesAction(action, jio_description); return this.run("get", arguments);
};
jio_leaves.allDocs = function () {
return this.run("allDocs", arguments);
}; };
// post a new document without id stop();
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 (util.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 // post a new document without id
o.doc._id = o.uuid; shared.doc = {"title": "post document without id"};
o.revisions = {"start": 0, "ids": []};
o.rev_hash = generateRevisionHash(o.doc, o.revisions); jio.post(shared.doc).then(function (answer) {
o.rev = "1-" + o.rev_hash;
o.leavesAction(function (storage_description, param) { shared.revisions = {"start": 0, "ids": []};
var suffix = "", doc = deepClone(o.doc); shared.uuid = answer.id;
if (param.revision) { shared.rev = answer.rev;
deepEqual(o.response_rev, o.rev, "Check revision"); shared.rev_hash = shared.rev.slice(2);
doc._id += "." + o.rev; shared.doc._id = shared.uuid;
suffix = "." + o.rev; ok(util.isUuid(shared.uuid), "Uuid should look like " +
"xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx : " + shared.uuid);
deepEqual(answer, {
"id": shared.uuid,
"method": "post",
"result": "success",
"rev": "1-" + generateRevisionHash(shared.doc, shared.revisions),
"status": 201,
"statusText": "Created"
}, "Post document (without id)");
delete shared.doc._id;
return jio_leaves.get({"_id": shared.uuid + "." + shared.rev});
}).then(function (answers) {
var i;
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", {
"_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, "conflicts": true,
"revs": true, "revs": true,
"revs_info": true "revs_info": true
}, o.f); });
o.tick(o);
}).then(function (answer) {
deepEqual(answer.data, {
"_id": shared.uuid,
"_rev": shared.rev,
"_revisions": {
"ids": [shared.rev_hash],
"start": 1
},
"_revs_info": [{
"rev": shared.rev,
"status": "available"
}],
"title": "post document without id"
}, "Get the generated document, the winner");
// post a new document with id // post a new document with id
o.doc = {"_id": "doc1", "title": "post new doc with id"}; shared.doc = {"_id": "doc1", "title": "post new doc with id"};
o.rev1_1_hash = generateRevisionHash(o.doc, o.revisions); shared.rev1_1_hash = generateRevisionHash(shared.doc, shared.revisions);
o.rev1_1 = "1-" + o.rev1_1_hash; shared.rev1_1 = "1-" + shared.rev1_1_hash;
o.rev1_1_history = {"start": 1, "ids": [o.rev1_1_hash]}; shared.rev1_1_history = {"start": 1, "ids": [shared.rev1_1_hash]};
o.rev1_1_revs_info = [{"rev": o.rev1_1, "status": "available"}]; shared.rev1_1_revs_info = [{"rev": shared.rev1_1, "status": "available"}];
o.spy(o, "value", {"ok": true, "id": "doc1", "rev": o.rev1_1},
"Post new document with an id"); return jio.post(shared.doc);
o.jio.post(o.doc, o.f);
o.tick(o); }).then(function (answer) {
deepEqual(answer, {
"id": "doc1",
"method": "post",
"result": "success",
"rev": shared.rev1_1,
"status": 201,
"statusText": "Created"
}, "Post new document with an id");
// / // /
// | // |
// 1-1 // 1-1
// check document // check document
o.leavesAction(function (storage_description, param) { return jio_leaves.get({"_id": "doc1." + shared.rev1_1});
var suffix = "", doc = deepClone(o.doc);
if (param.revision) { }).then(function (answers) {
doc._id += "." + o.rev1_1; var i;
suffix = "." + o.rev1_1; 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",
"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, "conflicts": true,
"revs": true, "revs": true,
"revs_info": true "revs_info": true
}, o.f); });
o.tick(o);
}).then(function (answer) {
deepEqual(answer.data, {
"_id": "doc1",
"_rev": shared.rev1_1,
"_revisions": {"start": 1, "ids": [shared.rev1_1_hash]},
"_revs_info": [{"rev": shared.rev1_1, "status": "available"}],
"title": "post new doc with id"
}, "Get the previous document (without revision)");
// post same document without revision // post same document without revision
o.doc = {"_id": "doc1", "title": "post same document without revision"}; shared.doc = {
o.rev1_2_hash = generateRevisionHash(o.doc, o.revisions); "_id": "doc1",
o.rev1_2 = "1-" + o.rev1_2_hash; "title": "post same document without revision"
o.rev1_2_history = {"start": 1, "ids": [o.rev1_2_hash]}; };
o.rev1_2_revs_info = [{"rev": o.rev1_2, "status": "available"}]; shared.rev1_2_hash = generateRevisionHash(shared.doc, shared.revisions);
o.spy(o, "value", {"ok": true, "id": "doc1", "rev": o.rev1_2}, shared.rev1_2 = "1-" + shared.rev1_2_hash;
"Post same document (without revision)"); shared.rev1_2_history = {"start": 1, "ids": [shared.rev1_2_hash]};
o.jio.post(o.doc, o.f); shared.rev1_2_revs_info = [{"rev": shared.rev1_2, "status": "available"}];
o.tick(o);
return jio.post(shared.doc);
}).then(function (answer) {
deepEqual(answer, {
"id": "doc1",
"method": "post",
"result": "success",
"rev": shared.rev1_2,
"status": 201,
"statusText": "Created"
}, "Post same document (without revision)");
// / // /
// / \ // / \
// 1-1 1-2 // 1-1 1-2
// check document // check document
o.leavesAction(function (storage_description, param) { return jio_leaves.get({"_id": "doc1." + shared.rev1_2});
var suffix = "", doc = deepClone(o.doc);
if (param.revision) { }).then(function (answers) {
doc._id += "." + o.rev1_2; var i;
suffix = "." + o.rev1_2; 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"
});
return jio.post(shared.doc);
}).then(function (answer) {
deepEqual(answer, {
"id": "doc1",
"method": "post",
"result": "success",
"rev": shared.rev2_3,
"status": 201,
"statusText": "Created"
}, "Post document (with revision)");
// / // /
// / \ // / \
...@@ -252,49 +298,57 @@ ...@@ -252,49 +298,57 @@
// 2-3 // 2-3
// check document // check document
o.leavesAction(function (storage_description, param) { return jio_leaves.get({"_id": "doc1." + shared.rev2_3});
var suffix = "", doc = deepClone(o.doc);
delete doc._rev; }).then(function (answers) {
if (param.revision) { var i;
doc._id += "." + o.rev2_3; for (i = 0; i < answers.length; i += 1) {
suffix = "." + o.rev2_3; 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",
"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, "conflicts": true,
"revs": true, "revs": true,
"revs_info": true "revs_info": true
}, o.f); });
o.tick(o);
// put document without id }).then(function (answer) {
o.spy(o, "status", 20, "Put document without id");
o.jio.put({}, o.f); deepEqual(answer.data, {
o.tick(o); "_id": "doc1",
"_rev": shared.rev1_2,
"_revisions": {"start": 1, "ids": [shared.rev1_2_hash]},
"_revs_info": [{"rev": shared.rev1_2, "status": "available"}],
"_conflicts": [shared.rev1_1],
"title": "post same document without revision"
}, "Get the previous document (with revision)");
// put document without rev // put document without rev
o.doc = {"_id": "doc1", "title": "put new document"}; shared.doc = {"_id": "doc1", "title": "put new document"};
o.rev1_4_hash = generateRevisionHash(o.doc, {"start": 0, "ids": []}); shared.rev1_4_hash = generateRevisionHash(shared.doc, {
o.rev1_4 = "1-" + o.rev1_4_hash; "start": 0,
o.rev1_4_history = {"start": 1, "ids": [o.rev1_4_hash]}; "ids": []
o.rev1_4_revs_info = [{"rev": o.rev1_4, "status": "available"}]; });
o.spy(o, "value", {"id": "doc1", "ok": true, "rev": o.rev1_4}, shared.rev1_4 = "1-" + shared.rev1_4_hash;
"Put document without rev"); shared.rev1_4_history = {"start": 1, "ids": [shared.rev1_4_hash]};
o.jio.put(o.doc, o.f); shared.rev1_4_revs_info = [{"rev": shared.rev1_4, "status": "available"}];
o.tick(o);
return jio.put(shared.doc);
}).then(function (answer) {
deepEqual(answer, {
"id": "doc1",
"method": "put",
"result": "success",
"rev": shared.rev1_4,
"status": 204,
"statusText": "No Content"
}, "Put document without rev");
// __/__ // __/__
// / | \ // / | \
...@@ -303,16 +357,36 @@ ...@@ -303,16 +357,36 @@
// 2-3 // 2-3
// put new revision // put new revision
o.doc = {"_id": "doc1", "title": "put new revision", "_rev": o.rev1_4}; shared.doc = {
o.rev2_5_hash = generateRevisionHash(o.doc, o.rev1_4_history); "_id": "doc1",
o.rev2_5 = "2-" + o.rev2_5_hash; "title": "put new revision",
o.rev2_5_history = {"start": 2, "ids": [o.rev2_5_hash, o.rev1_4_hash]}; "_rev": shared.rev1_4
o.rev2_5_revs_info = deepClone(o.rev1_4_revs_info); };
o.rev2_5_revs_info.unshift({"rev": o.rev2_5, "status": "available"}); shared.rev2_5_hash =
o.spy(o, "value", {"id": "doc1", "ok": true, "rev": o.rev2_5}, generateRevisionHash(shared.doc, shared.rev1_4_history);
"Put new revision"); shared.rev2_5 = "2-" + shared.rev2_5_hash;
o.jio.put(o.doc, o.f); shared.rev2_5_history = {
o.tick(o); "start": 2,
"ids": [shared.rev2_5_hash, shared.rev1_4_hash]
};
shared.rev2_5_revs_info = tool.deepClone(shared.rev1_4_revs_info);
shared.rev2_5_revs_info.unshift({
"rev": shared.rev2_5,
"status": "available"
});
return jio.put(shared.doc);
}).then(function (answer) {
deepEqual(answer, {
"id": "doc1",
"method": "put",
"result": "success",
"rev": shared.rev2_5,
"status": 204,
"statusText": "No Content"
}, "Put new revision");
// __/__ // __/__
// / | \ // / | \
...@@ -321,43 +395,67 @@ ...@@ -321,43 +395,67 @@
// 2-3 2-5 // 2-3 2-5
// putAttachment to inexistent document // putAttachment to inexistent document
o.doc = { shared.doc = {
"_id": "doc2", "_id": "doc2",
"_mimetype": "text/plain", "_content_type": "text/plain",
"_data": "doc 2 - attachment 1", "_data": "doc 2 - attachment 1",
"_attachment": "attachment1" "_attachment": "attachment1"
}; };
o.rev_hash = generateRevisionHash(o.doc, {"start": 0, "ids": []}); shared.rev_hash = generateRevisionHash(shared.doc, {
o.rev = "1-" + o.rev_hash; "start": 0,
o.spy(o, "value", "ids": []
{"ok": true, "id": "doc2", "attachment": "attachment1", "rev": o.rev}, });
"Put an attachment to an inexistent document"); shared.rev = "1-" + shared.rev_hash;
o.jio.putAttachment(o.doc, o.f);
o.tick(o); return jio.putAttachment(shared.doc);
}).then(function (answer) {
deepEqual(answer, {
"attachment": "attachment1",
"id": "doc2",
"method": "putAttachment",
"result": "success",
"rev": shared.rev,
"status": 204,
"statusText": "No Content"
}, "Put an attachment to an inexistent document");
// putAttachment // putAttachment
o.doc = { shared.doc = {
"_id": "doc1", "_id": "doc1",
"_mimetype": "text/plain", "_content_type": "text/plain",
"_data": "doc 1 - attachment 1", "_data": "doc 1 - attachment 1",
"_attachment": "attachment1", "_attachment": "attachment1",
"_rev": o.rev2_5 "_rev": shared.rev2_5
}; };
o.rev3_6_hash = generateRevisionHash(o.doc, o.rev2_5_history); shared.attmt1_digest = "sha256-7b6f6ec759b90a0d2aea0b2a6172544c904c6722" +
o.rev3_6 = "3-" + o.rev3_6_hash; "1a04fb871477825db92c42ff";
o.rev3_6_history = deepClone(o.rev2_5_history); shared.rev3_6_hash =
o.rev3_6_history.start += 1; generateRevisionHash(shared.doc, shared.rev2_5_history);
o.rev3_6_history.ids.unshift(o.rev3_6_hash); shared.rev3_6 = "3-" + shared.rev3_6_hash;
o.rev3_6_revs_info = deepClone(o.rev2_5_revs_info); shared.rev3_6_history = tool.deepClone(shared.rev2_5_history);
o.rev3_6_revs_info.unshift({"rev": o.rev3_6, "status": "available"}); shared.rev3_6_history.start += 1;
o.spy(o, "value", { shared.rev3_6_history.ids.unshift(shared.rev3_6_hash);
"ok": true, shared.rev3_6_revs_info = tool.deepClone(shared.rev2_5_revs_info);
"id": "doc1", shared.rev3_6_revs_info.unshift({
"rev": shared.rev3_6,
"status": "available"
});
return jio.putAttachment(shared.doc);
}).then(function (answer) {
deepEqual(answer, {
"attachment": "attachment1", "attachment": "attachment1",
"rev": o.rev3_6 "id": "doc1",
"method": "putAttachment",
"result": "success",
"rev": shared.rev3_6,
"status": 204,
"statusText": "No Content"
}, "Put an attachment to the first document"); }, "Put an attachment to the first document");
o.jio.putAttachment(o.doc, o.f);
o.tick(o);
// __/__ // __/__
// / | \ // / | \
...@@ -368,55 +466,73 @@ ...@@ -368,55 +466,73 @@
// 3-6+a1 // 3-6+a1
// get document // get document
o.doc = { return jio.get({"_id": "doc1"}, {
"conflicts": true,
"revs": true,
"revs_info": true
});
}).then(function (answer) {
deepEqual(answer.data, {
"_id": "doc1", "_id": "doc1",
"_rev": o.rev3_6, "_rev": shared.rev3_6,
"_revisions": o.rev3_6_history, "_revisions": shared.rev3_6_history,
"_revs_info": o.rev3_6_revs_info, "_revs_info": shared.rev3_6_revs_info,
"_conflicts": [o.rev2_3, o.rev1_1], "_conflicts": [shared.rev2_3, shared.rev1_1],
"_attachments": { "_attachments": {
"attachment1": { "attachment1": {
"length": "doc 1 - attachment 1".length, "length": "doc 1 - attachment 1".length,
"content_type": "text/plain", "content_type": "text/plain",
"digest": "md5-0505c1fb6aae02dd1695d33841726564" "digest": shared.attmt1_digest
} }
}, },
"title": "put new revision" "title": "put new revision"
}; }, "Get document, the winner");
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 // get winner attachment
o.doc = { return jio.getAttachment({
"_id": "doc1", "_id": "doc1",
"_attachment": "attachment1" "_attachment": "attachment1"
}; });
o.spy(o, "value", "doc 1 - attachment 1", "Get the winner's attachment");
o.jio.getAttachment(o.doc, o.f); }).then(function (answer) {
o.tick(o); return tool.readBlobAsBinaryString(answer.data);
}).then(function (event) {
deepEqual(event.target.result, "doc 1 - attachment 1",
"Get the winner's attachment");
// put document // put document
o.doc = { shared.doc = {
"_id": "doc1", "_id": "doc1",
"_rev": o.rev3_6, "_rev": shared.rev3_6,
"title": "Put revision, attachment must be copied" "title": "Put revision, attachment must be copied"
}; };
o.rev4_7_hash = generateRevisionHash(o.doc, o.rev3_6_history); shared.rev4_7_hash =
o.rev4_7 = "4-" + o.rev4_7_hash; generateRevisionHash(shared.doc, shared.rev3_6_history);
o.rev4_7_history = deepClone(o.rev3_6_history); shared.rev4_7 = "4-" + shared.rev4_7_hash;
o.rev4_7_history.start += 1; shared.rev4_7_history = tool.deepClone(shared.rev3_6_history);
o.rev4_7_history.ids.unshift(o.rev4_7_hash); shared.rev4_7_history.start += 1;
o.rev4_7_revs_info = deepClone(o.rev3_6_revs_info); shared.rev4_7_history.ids.unshift(shared.rev4_7_hash);
o.rev4_7_revs_info.unshift({"rev": o.rev4_7, "status": "available"}); shared.rev4_7_revs_info = tool.deepClone(shared.rev3_6_revs_info);
o.spy(o, "value", {"ok": true, "id": "doc1", "rev": o.rev4_7}, shared.rev4_7_revs_info.unshift({
"Update document, attachment should be copied"); "rev": shared.rev4_7,
o.jio.put(o.doc, o.f); "status": "available"
o.tick(o); });
return jio.put(shared.doc);
}).then(function (answer) {
deepEqual(answer, {
"id": "doc1",
"method": "put",
"result": "success",
"rev": shared.rev4_7,
"status": 204,
"statusText": "No Content"
}, "Update document, attachment should be copied");
// __/__ // __/__
// / | \ // / | \
...@@ -429,62 +545,77 @@ ...@@ -429,62 +545,77 @@
// 4-7+a1 // 4-7+a1
// get document, attachment must be copied // get document, attachment must be copied
o.doc = { shared.doc = {
"_id": "doc1", "_id": "doc1",
"_rev": o.rev4_7, "_rev": shared.rev4_7,
"title": o.doc.title, "title": shared.doc.title,
"_attachments": { "_attachments": {
"attachment1": { "attachment1": {
"length": "doc 1 - attachment 1".length, "length": "doc 1 - attachment 1".length,
"content_type": "text/plain", "content_type": "text/plain",
"digest": "md5-0505c1fb6aae02dd1695d33841726564" "digest": shared.attmt1_digest
} }
}, },
"_conflicts": [o.rev2_3, o.rev1_1], "_conflicts": [shared.rev2_3, shared.rev1_1],
"_revisions": o.rev4_7_history, "_revisions": shared.rev4_7_history,
"_revs_info": o.rev4_7_revs_info "_revs_info": shared.rev4_7_revs_info
}; };
o.spy(o, "value", o.doc,
"Get the new winner document and its attachment metadata"); return jio.get({"_id": "doc1"}, {
o.jio.get({"_id": "doc1"}, {
"conflicts": true, "conflicts": true,
"revs": true, "revs": true,
"revs_info": true "revs_info": true
}, o.f); });
o.tick(o);
// get attachment }).then(function (answer) {
o.doc = {
deepEqual(answer.data, shared.doc,
"Get the new winner document and its attachment metadata");
// get winner attachment
return jio.getAttachment({
"_id": "doc1", "_id": "doc1",
"_attachment": "attachment1" "_attachment": "attachment1"
}; });
o.spy(o, "value", "doc 1 - attachment 1",
}).then(function (answer) {
return tool.readBlobAsBinaryString(answer.data);
}).then(function (event) {
deepEqual(event.target.result, "doc 1 - attachment 1",
"Get the winner's attachment again"); "Get the winner's attachment again");
o.jio.getAttachment(o.doc, o.f);
o.tick(o);
// remove attachment // remove attachment
o.doc = { shared.doc = {
"_id": "doc1", "_id": "doc1",
"_attachment": "attachment1", "_attachment": "attachment1",
"_rev": o.rev4_7 "_rev": shared.rev4_7
}; };
o.rev5_8_hash = generateRevisionHash(o.doc, o.rev4_7_history); shared.rev5_8_hash =
o.rev5_8 = "5-" + o.rev5_8_hash; generateRevisionHash(shared.doc, shared.rev4_7_history);
o.rev5_8_history = deepClone(o.rev4_7_history); shared.rev5_8 = "5-" + shared.rev5_8_hash;
o.rev5_8_history.start += 1; shared.rev5_8_history = tool.deepClone(shared.rev4_7_history);
o.rev5_8_history.ids.unshift(o.rev5_8_hash); shared.rev5_8_history.start += 1;
o.rev5_8_revs_info = deepClone(o.rev4_7_revs_info); shared.rev5_8_history.ids.unshift(shared.rev5_8_hash);
o.rev5_8_revs_info.unshift({"rev": o.rev5_8, "status": "available"}); shared.rev5_8_revs_info = tool.deepClone(shared.rev4_7_revs_info);
o.spy(o, "value", { shared.rev5_8_revs_info.unshift({
"ok": true, "rev": shared.rev5_8,
"id": "doc1", "status": "available"
});
return jio.removeAttachment(shared.doc);
}).then(function (answer) {
deepEqual(answer, {
"attachment": "attachment1", "attachment": "attachment1",
"rev": o.rev5_8 "id": "doc1",
"method": "removeAttachment",
"result": "success",
"rev": shared.rev5_8,
"status": 204,
"statusText": "No Content"
}, "Remove attachment"); }, "Remove attachment");
o.jio.removeAttachment(o.doc, o.f);
o.tick(o);
// __/__ // __/__
// / | \ // / | \
...@@ -499,78 +630,116 @@ ...@@ -499,78 +630,116 @@
// 5-8 // 5-8
// get document to check attachment existence // get document to check attachment existence
o.doc = { shared.doc = {
"_id": "doc1", "_id": "doc1",
"_rev": o.rev5_8, "_rev": shared.rev5_8,
"title": "Put revision, attachment must be copied", "title": "Put revision, attachment must be copied",
"_conflicts": [o.rev2_3, o.rev1_1], "_conflicts": [shared.rev2_3, shared.rev1_1],
"_revisions": o.rev5_8_history, "_revisions": shared.rev5_8_history,
"_revs_info": o.rev5_8_revs_info "_revs_info": shared.rev5_8_revs_info
}; };
o.spy(o, "value", o.doc,
"Get the new winner document, no attachment must be provided"); return jio.get({"_id": "doc1"}, {
o.jio.get({"_id": "doc1"}, {
"conflicts": true, "conflicts": true,
"revs": true, "revs": true,
"revs_info": true "revs_info": true
}, o.f); });
o.tick(o);
}).then(function (answer) {
deepEqual(answer.data, shared.doc,
"Get the new winner document, no attachment must be provided");
// get specific document // get specific document
o.doc = { shared.doc = {
"_id": "doc1", "_id": "doc1",
"_rev": o.rev4_7, "_rev": shared.rev4_7,
"title": o.doc.title, "title": shared.doc.title,
"_attachments": { "_attachments": {
"attachment1": { "attachment1": {
"length": "doc 1 - attachment 1".length, "length": "doc 1 - attachment 1".length,
"content_type": "text/plain", "content_type": "text/plain",
"digest": "md5-0505c1fb6aae02dd1695d33841726564" "digest": shared.attmt1_digest
} }
}, },
"_conflicts": [o.rev2_3, o.rev1_1], "_conflicts": [shared.rev2_3, shared.rev1_1],
"_revisions": o.rev4_7_history, "_revisions": shared.rev4_7_history,
"_revs_info": o.rev4_7_revs_info "_revs_info": shared.rev4_7_revs_info
}; };
o.spy(o, "value", o.doc,
"Get the new winner document and its attachment metadata"); return jio.get({"_id": "doc1", "_rev": shared.rev4_7}, {
o.jio.get({"_id": "doc1", "_rev": o.rev4_7}, {
"conflicts": true, "conflicts": true,
"revs": true, "revs": true,
"revs_info": true "revs_info": true
}, o.f); });
o.tick(o);
}).then(function (answer) {
deepEqual(answer.data, shared.doc,
"Get specific revision and its attachment metadata");
// get inexistent attachment // get inexistent attachment
o.spy(o, "status", 404, "Get inexistent winner attachment" + return reverse(jio.getAttachment({
" -> 404 Not Found"); "_id": "doc1",
o.jio.get({"_id": "doc1/attachment1"}, o.f); "_attachment": "attachment1"
o.tick(o); }));
}).then(function (answer) {
deepEqual(answer, {
"attachment": "attachment1",
"error": "not_found",
"id": "doc1",
"message": "Unable to get an inexistent attachment",
"method": "getAttachment",
"reason": "missing",
"result": "error",
"status": 404,
"statusText": "Not Found"
}, "Get inexistent winner attachment -> 404 Not Found");
// get specific attachment // get specific attachment
o.doc = { shared.doc = {
"_id": "doc1", "_id": "doc1",
"_attachment": "attachment1", "_attachment": "attachment1",
"_rev": o.rev3_6 "_rev": shared.rev3_6
}; };
o.spy(o, "value", "doc 1 - attachment 1", "Get a specific attachment"); return jio.getAttachment(shared.doc);
o.jio.getAttachment(o.doc, o.f);
o.tick(o); }).then(function (answer) {
return tool.readBlobAsBinaryString(answer.data);
}).then(function (event) {
deepEqual(event.target.result, "doc 1 - attachment 1",
"Get a specific attachment");
// remove specific document and conflict // remove specific document and conflict
o.doc = {"_id": "doc1", "_rev": o.rev1_1}; shared.doc = {"_id": "doc1", "_rev": shared.rev1_1};
// generate with deleted_flag // generate with deleted_flag
o.rev2_9_hash = generateRevisionHash(o.doc, o.rev1_1_history, true); shared.rev2_9_hash =
o.rev2_9 = "2-" + o.rev2_9_hash; generateRevisionHash(shared.doc, shared.rev1_1_history, true);
o.rev2_9_history = deepClone(o.rev1_1_history); shared.rev2_9 = "2-" + shared.rev2_9_hash;
o.rev2_9_history.start += 1; shared.rev2_9_history = tool.deepClone(shared.rev1_1_history);
o.rev2_9_history.ids.unshift(o.rev2_9_hash); shared.rev2_9_history.start += 1;
o.rev2_9_revs_info = deepClone(o.rev1_1_revs_info); shared.rev2_9_history.ids.unshift(shared.rev2_9_hash);
o.rev2_9_revs_info.unshift({"rev": o.rev2_9, "status": "deleted"}); shared.rev2_9_revs_info = tool.deepClone(shared.rev1_1_revs_info);
o.spy(o, "value", {"ok": true, "id": "doc1", "rev": o.rev2_9}, shared.rev2_9_revs_info.unshift({
"Remove specific document, and one conflict"); "rev": shared.rev2_9,
o.jio.remove(o.doc, o.f); "status": "deleted"
o.tick(o); });
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");
// __/___ // __/___
// / | \ // / | \
...@@ -585,18 +754,31 @@ ...@@ -585,18 +754,31 @@
// 5-8 // 5-8
// remove specific document and conflict // remove specific document and conflict
o.doc = {"_id": "doc1", "_rev": o.rev2_3}; shared.doc = {"_id": "doc1", "_rev": shared.rev2_3};
o.rev3_10_hash = generateRevisionHash(o.doc, o.rev2_3_history, true); shared.rev3_10_hash =
o.rev3_10 = "3-" + o.rev3_10_hash; generateRevisionHash(shared.doc, shared.rev2_3_history, true);
o.rev3_10_history = deepClone(o.rev2_3_history); shared.rev3_10 = "3-" + shared.rev3_10_hash;
o.rev3_10_history.start += 1; shared.rev3_10_history = tool.deepClone(shared.rev2_3_history);
o.rev3_10_history.ids.unshift(o.rev3_10_hash); shared.rev3_10_history.start += 1;
o.rev3_10_revs_info = deepClone(o.rev2_3_revs_info); shared.rev3_10_history.ids.unshift(shared.rev3_10_hash);
o.rev3_10_revs_info.unshift({"rev": o.rev3_10, "status": "deleted"}); shared.rev3_10_revs_info = tool.deepClone(shared.rev2_3_revs_info);
o.spy(o, "value", {"ok": true, "id": "doc1", "rev": o.rev3_10}, shared.rev3_10_revs_info.unshift({
"Remove specific document, and one conflict"); "rev": shared.rev3_10,
o.jio.remove(o.doc, o.f); "status": "deleted"
o.tick(o); });
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");
// ___/____ // ___/____
// / | \ // / | \
...@@ -611,33 +793,46 @@ ...@@ -611,33 +793,46 @@
// 5-8 // 5-8
// get document no more conflict // get document no more conflict
o.doc = { shared.doc = {
"_id": "doc1", "_id": "doc1",
"_rev": o.rev5_8, "_rev": shared.rev5_8,
"title": "Put revision, attachment must be copied", "title": "Put revision, attachment must be copied",
"_revisions": o.rev5_8_history, "_revisions": shared.rev5_8_history,
"_revs_info": o.rev5_8_revs_info "_revs_info": shared.rev5_8_revs_info
}; };
o.spy(o, "value", o.doc,
"Get the new winner document, no more conflicts"); return jio.get({"_id": "doc1"}, {
o.jio.get({"_id": "doc1"}, {
"conflicts": true, "conflicts": true,
"revs": true, "revs": true,
"revs_info": true "revs_info": true
}, o.f); });
o.tick(o);
}).then(function (answer) {
deepEqual(answer.data, shared.doc,
"Get the new winner document, no more conflicts");
// remove document // remove document
o.doc = { shared.doc = {
"_id": "doc1", "_id": "doc1",
"_rev": o.rev5_8 "_rev": shared.rev5_8
}; };
o.rev6_11_hash = generateRevisionHash(o.doc, o.rev5_8_history, true); shared.rev6_11_hash =
o.rev6_11 = "6-" + o.rev6_11_hash; generateRevisionHash(shared.doc, shared.rev5_8_history, true);
o.spy(o, "value", {"ok": true, "id": "doc1", "rev": o.rev6_11}, shared.rev6_11 = "6-" + shared.rev6_11_hash;
"Remove the last document");
o.jio.remove(o.doc, o.f); return jio.remove(shared.doc);
o.tick(o);
}).then(function (answer) {
deepEqual(answer, {
"id": "doc1",
"method": "remove",
"result": "success",
"rev": shared.rev6_11,
"status": 204,
"statusText": "No Content"
}, "Remove the last document");
// ___/____ // ___/____
// / | \ // / | \
...@@ -654,51 +849,84 @@ ...@@ -654,51 +849,84 @@
// D6-11 // D6-11
// get inexistent document // get inexistent document
o.spy(o, "status", 404, "Get inexistent document -> 404 Not Found"); return reverse(jio.get({"_id": "doc3"}, {
o.jio.get({"_id": "doc3"}, {
"conflicts": true, "conflicts": true,
"revs": true, "revs": true,
"revisions": true "revisions": true
}, o.f); }));
o.tick(o);
}).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 // get specific deleted document
o.spy(o, "status", 404, "Get deleted document -> 404 Not Found"); return reverse(jio.get({"_id": "doc1", "rev": shared.rev3_10}, {
o.jio.get({"_id": "doc1", "rev": o.rev3_10}, {
"conflicts": true, "conflicts": true,
"revs": true, "revs": true,
"revs_info": true "revs_info": true
}, o.f); }));
o.tick(o);
// get specific deleted document }).then(function (answer) {
o.spy(o, "status", 404, "Get deleted document -> 404 Not Found");
o.jio.get({"_id": "doc1"}, { 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, "conflicts": true,
"revs": true, "revs": true,
"revs_info": true "revs_info": true
}, o.f); }));
o.tick(o);
util.closeAndcleanUpJio(o.jio); }).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,
index
) {
/*jslint unparam: true */
var o = generateTools();
o.jio = jIO.newJio(description); var shared = {}, jio, jio_leaves = [];
o.localpath1 = "jio/localstorage/usyncreprevlocloc1/" + index; shared.workspace = {};
o.localpath2 = "jio/localstorage/usyncreprevlocloc2/" + index; jio = jIO.createJIO(jio_description, {"workspace": shared.workspace});
o.localpath3 = "jio/localstorage/usyncreprevlocloc3/" + index;
o.localpath4 = "jio/localstorage/usyncreprevlocloc4/" + index; 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);
};
stop();
// 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 // no synchronisation
o.spy(o, "value", {"ok": true, "id": "doc1"}, return jio.check({"_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"}, }).then(function (answer) {
"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"}, deepEqual(answer, {
"Repair document"); "id": "doc1",
o.jio.repair({"_id": "doc1"}, o.f); "method": "check",
o.tick(o); "result": "success",
"status": 204,
"statusText": "No Content"
}, "Check document");
return jio.check({"_id": "doc1", "_rev": "1-111"});
}).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 // check documents from localstorage
deepEqual( return jio_leaves.get({"_id": "doc1.revision_tree.json"});
util.jsonlocalstorage.getItem(o.localpath1 + "/doc1.revision_tree.json"),
o.doctree1_1, }).then(function (answers) {
"Check revision tree 1, no synchro done" var i;
); for (i = 0; i < answers.length; i += 1) {
deepEqual( deepEqual(answers[i].data, shared.doctree1_1, "Check revision tree " +
util.jsonlocalstorage.getItem(o.localpath2 + "/doc1.revision_tree.json"), i + ", no syncho done");
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 // add documents to localstorage
o.doctree2_2 = deepClone(o.doctree1_1); shared.doctree2_2 = tool.deepClone(shared.doctree1_1);
o.doctree2_2.children[0].children.push({ shared.doctree2_2.children = JSON.parse(shared.doctree2_2.children);
shared.doctree2_2.children[0].children.push({
"rev": "2-222", "rev": "2-222",
"status": "available", "status": "available",
"children": [] "children": []
}); });
o.doc2_2 = { shared.doctree2_2.children = JSON.stringify(shared.doctree2_2.children);
shared.doc2_2 = {
"_id": "doc1.2-222", "_id": "doc1.2-222",
"title": "B", "title": "B",
"_attachments": { "_attachments": {
"haha": { "haha": {
"length": 3, "length": 3,
"digest": "md5-900150983cd24fb0d6963f7d28e17f72", "digest": "sha256-ba7816bf8f01cfea414140de5dae2223b00361a3" +
"96177a9cb410ff61f20015ad",
"content_type": "text/plain" "content_type": "text/plain"
} }
} }
}; };
util.jsonlocalstorage.setItem(o.localpath1 + "/doc1.revision_tree.json",
o.doctree2_2); return jio_leaves[0].put(shared.doctree2_2);
util.jsonlocalstorage.setItem(o.localpath1 + "/" + o.doc2_2._id, o.doc2_2); }).then(function () {
util.jsonlocalstorage.setItem(o.localpath1 + "/" + o.doc2_2._id + return jio_leaves[0].put(shared.doc2_2);
"/haha", "abc"); }).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 // document synchronisation without conflict
o.spy(o, "status", 41, "Check document"); return reverse(jio.check({"_id": "doc1"}));
o.jio.check({"_id": "doc1"}, o.f);
o.tick(o, 50000);
o.spy(o, "value", {"ok": true, "id": "doc1"}, }).then(function (answer) {
"Repair document");
o.jio.repair({"_id": "doc1"}, o.f);
o.tick(o, 50000);
// check documents from localstorage deepEqual(answer, {
deepEqual( "error": "conflict",
util.jsonlocalstorage.getItem(o.localpath1 + "/doc1.revision_tree.json"), "id": "doc1",
o.doctree2_2, "message": "Some documents are different in the sub storages",
"Check revision tree 1, no synchro done" "method": "check",
); "reason": "Storage contents differ",
deepEqual( "result": "error",
util.jsonlocalstorage.getItem(o.localpath2 + "/doc1.revision_tree.json"), "status": 409,
o.doctree2_2, "statusText": "Conflict"
"Check revision tree 2, revision synchro done" }, "Check document");
);
deepEqual( return jio.repair({"_id": "doc1"});
util.jsonlocalstorage.getItem(o.localpath3 + "/doc1.revision_tree.json"),
o.doctree2_2, }).then(function (answer) {
"Check revision tree 3, revision synchro done"
); deepEqual(answer, {
deepEqual( "id": "doc1",
util.jsonlocalstorage.getItem(o.localpath3 + "/doc1.2-222"), "method": "repair",
o.doc2_2, "result": "success",
"Check document 3" "status": 204,
); "statusText": "No Content"
deepEqual( }, "Repair document");
util.jsonlocalstorage.getItem(o.localpath3 + "/doc1.2-222/haha"),
"abc", // check document trees
"Check attachment 3" return jio_leaves.get({"_id": "doc1.revision_tree.json"});
);
deepEqual( }).then(function (answers) {
util.jsonlocalstorage.getItem(o.localpath4 + "/doc1.revision_tree.json"), var i;
o.doctree2_2, for (i = 0; i < answers.length; i += 1) {
"Check revision tree 4, revision synchro done" deepEqual(answers[i].data, shared.doctree2_2, "Check revision tree " +
); i + ", " + (i ? "" : "no") + " synchro done");
}
// check document 2
return jio_leaves[2].get({"_id": "doc1.2-222"});
}).then(function (answer) {
deepEqual(answer.data, shared.doc2_2, "Check document 2");
// check attachment 2
return jio_leaves[2].getAttachment({
"_id": "doc1.2-222",
"_attachment": "haha"
});
}).then(function (answer) {
return tool.readBlobAsBinaryString(answer.data);
}).then(function (event) {
deepEqual(event.target.result, "abc", "Check attachment 2");
// add documents to localstorage // add documents to localstorage
o.doctree2_3 = deepClone(o.doctree2_2); shared.doctree2_3 = tool.deepClone(shared.doctree2_2);
o.doctree2_3.children[0].children.unshift({ shared.doctree2_3.children = JSON.parse(shared.doctree2_3.children);
shared.doctree2_3.children[0].children.unshift({
"rev": "2-223", "rev": "2-223",
"status": "available", "status": "available",
"children": [] "children": []
}); });
o.doc2_3 = {"_id": "doc1.2-223", "title": "C"}; shared.doctree2_3.children = JSON.stringify(shared.doctree2_3.children);
util.jsonlocalstorage.setItem(o.localpath1 + "/doc1.revision_tree.json", shared.doc2_3 = {"_id": "doc1.2-223", "title": "C"};
o.doctree2_3);
util.jsonlocalstorage.setItem(o.localpath1 + "/" + o.doc2_3._id, o.doc2_3); return jio_leaves[0].put(shared.doctree2_3);
}).then(function () {
return jio_leaves[0].put(shared.doc2_3);
}).then(function () {
// document synchronisation with conflict // document synchronisation with conflict
o.spy(o, "status", 41, "Check document"); return reverse(jio.check({"_id": "doc1"}));
o.jio.check({"_id": "doc1"}, o.f);
o.tick(o, 50000); }).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");
o.spy(o, "value", {"ok": true, "id": "doc1"}, return jio.repair({"_id": "doc1"});
"Repair document");
o.jio.repair({"_id": "doc1"}, o.f); }).then(function (answer) {
o.tick(o, 50000);
deepEqual(answer, {
"id": "doc1",
"method": "repair",
"result": "success",
"status": 204,
"statusText": "No Content"
}, "Repair document");
// check documents from localstorage // check documents from localstorage
deepEqual( return jio_leaves.get({"_id": "doc1.revision_tree.json"});
util.jsonlocalstorage.getItem(o.localpath1 + "/doc1.revision_tree.json"),
o.doctree2_3,
"Check revision tree 1, rev synchro"
);
deepEqual(
util.jsonlocalstorage.getItem(o.localpath2 + "/doc1.revision_tree.json"),
o.doctree2_3,
"Check revision tree 2, rev synchro"
);
deepEqual(
util.jsonlocalstorage.getItem(o.localpath3 + "/doc1.revision_tree.json"),
o.doctree2_3,
"Check revision tree 3, rev synchro"
);
deepEqual(
util.jsonlocalstorage.getItem(o.localpath3 + "/doc1.2-223"),
o.doc2_3,
"Check document 3"
);
deepEqual(
util.jsonlocalstorage.getItem(o.localpath4 + "/doc1.revision_tree.json"),
o.doctree2_3,
"Check revision tree 4, rev synchro"
);
util.closeAndcleanUpJio(o.jio); }).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