Commit 9cc2f37f authored by Tristan Cavelier's avatar Tristan Cavelier

indexstorage updated to JIO v2

parent 3a82fb20
...@@ -17,7 +17,7 @@ ...@@ -17,7 +17,7 @@
*/ */
/*jslint indent: 2, maxlen: 80, sloppy: true, nomen: true, regexp: true */ /*jslint indent: 2, maxlen: 80, sloppy: true, nomen: true, regexp: true */
/*global jIO, define, complex_queries */ /*global window, exports, require, define, jIO, RSVP, complex_queries */
/** /**
* JIO Index Storage. * JIO Index Storage.
...@@ -118,131 +118,23 @@ ...@@ -118,131 +118,23 @@
if (typeof define === 'function' && define.amd) { if (typeof define === 'function' && define.amd) {
return define(dependencies, module); return define(dependencies, module);
} }
module(jIO, complex_queries); if (typeof exports === 'object') {
}(['jio', 'complex_queries'], function (jIO, complex_queries) { return module(
"use strict"; exports,
require('jio'),
var error_dict = { require('rsvp'),
"Corrupted Index": { require('complex_queries')
"status": 24, );
"statusText": "Corrupt",
"error": "corrupt",
"reason": "corrupted index database"
},
"Corrupted Metadata": {
"status": 24,
"statusText": "Corrupt",
"error": "corrupt",
"reason": "corrupted document"
},
"Not Found": {
"status": 404,
"statusText": "Not Found",
"error": "not_found",
"reason": "missing document"
},
"Conflict": {
"status": 409,
"statusText": "Conflicts",
"error": "conflicts",
"reason": "already exist"
},
"Different Index": {
"status": 40,
"statusText": "Check failed",
"error": "check_failed",
"reason": "incomplete database"
}
};
/**
* Generate a JIO Error Object
*
* @method generateErrorObject
* @param {String} name The error name
* @param {String} message The error message
* @param {String} [reason] The error reason
* @return {Object} A jIO error object
*/
function generateErrorObject(name, message, reason) {
if (!error_dict[name]) {
return {
"status": 0,
"statusText": "Unknown",
"error": "unknown",
"message": message,
"reason": reason || "unknown"
};
}
return {
"status": error_dict[name].status,
"statusText": error_dict[name].statusText,
"error": error_dict[name].error,
"message": message,
"reason": reason || error_dict[name].reason
};
}
/**
* Get the real type of an object
* @method type
* @param {Any} value The value to check
* @return {String} The value type
*/
function type(value) {
// returns "String", "Object", "Array", "RegExp", ...
return (/^\[object ([a-zA-Z]+)\]$/).exec(
Object.prototype.toString.call(value)
)[1];
}
/**
* Generate a new uuid
* @method generateUuid
* @return {string} The new uuid
*/
function generateUuid() {
var S4 = function () {
var i, string = Math.floor(
Math.random() * 0x10000 /* 65536 */
).toString(16);
for (i = string.length; i < 4; i += 1) {
string = "0" + string;
}
return string;
};
return S4() + S4() + "-" +
S4() + "-" +
S4() + "-" +
S4() + "-" +
S4() + S4() + S4();
}
/**
* Tool to get the date in W3C date format "2011-12-13T14:15:16+01:00"
*
* @param {Any} date The new Date() parameter
* @return {String} The date in W3C date format
*/
function w3cDate(date) {
var d = new Date(date), offset = -d.getTimezoneOffset();
return (
d.getFullYear() + "-" +
(d.getMonth() + 1) + "-" +
d.getDate() + "T" +
d.getHours() + ":" +
d.getMinutes() + ":" +
d.getSeconds() +
(offset < 0 ? "-" : "+") +
(offset / 60) + ":" +
(offset % 60)
).replace(/[0-9]+/g, function (found) {
if (found.length < 2) {
return '0' + found;
}
return found;
});
} }
window.index_storage = {};
module(window.index_storage, jIO, RSVP, complex_queries);
}([
'exports',
'jio',
'rsvp',
'complex_queries'
], function (exports, jIO, RSVP, complex_queries) {
"use strict";
/** /**
* A JSON Index manipulator * A JSON Index manipulator
...@@ -314,7 +206,7 @@ ...@@ -314,7 +206,7 @@
*/ */
that.put = function (meta) { that.put = function (meta) {
var k, needed_meta = {}, ok = false; var k, needed_meta = {}, ok = false;
if (typeof meta._id !== "string" && meta._id !== "") { if (typeof meta._id !== "string" || meta._id === "") {
throw new TypeError("Corrupted Metadata"); throw new TypeError("Corrupted Metadata");
} }
for (k in meta) { for (k in meta) {
...@@ -359,7 +251,8 @@ ...@@ -359,7 +251,8 @@
throw new TypeError("Corrupted Metadata"); throw new TypeError("Corrupted Metadata");
} }
if (typeof that._location[meta._id] !== "number") { if (typeof that._location[meta._id] !== "number") {
throw new ReferenceError("Not Found"); // throw new ReferenceError("Not Found");
return;
} }
that._database[that._location[meta._id]] = null; that._database[that._location[meta._id]] = null;
that._free.push(that._location[meta._id]); that._free.push(that._location[meta._id]);
...@@ -388,7 +281,8 @@ ...@@ -388,7 +281,8 @@
for (id in that._location) { for (id in that._location) {
if (that._location.hasOwnProperty(id)) { if (that._location.hasOwnProperty(id)) {
database_meta = that._database[that._location[id]]; database_meta = that._database[that._location[id]];
if (type(database_meta) !== "Object" || if (typeof database_meta !== 'object' ||
Object.getPrototypeOf(database_meta || []) !== Object.prototype ||
database_meta._id !== id) { database_meta._id !== id) {
throw new TypeError("Corrupted Index"); throw new TypeError("Corrupted Index");
} }
...@@ -421,8 +315,11 @@ ...@@ -421,8 +315,11 @@
that.checkDocument = function (doc) { that.checkDocument = function (doc) {
var i, key, db_doc; var i, key, db_doc;
if (typeof that._location[doc._id] !== "number" || if (typeof that._location[doc._id] !== "number") {
(db_doc = that._database(that._location[doc._id])._id) !== doc._id) { throw new TypeError("Different Index");
}
db_doc = that._database(that._location[doc._id])._id;
if (db_doc !== doc._id) {
throw new TypeError("Different Index"); throw new TypeError("Different Index");
} }
for (i = 0; i < that._indexing.length; i += 1) { for (i = 0; i < that._indexing.length; i += 1) {
...@@ -442,12 +339,13 @@ ...@@ -442,12 +339,13 @@
var i = 0, meta; var i = 0, meta;
that._free = []; that._free = [];
that._location = {}; that._location = {};
if (type(that._database) !== "Array") { if (!Array.isArray(that._database)) {
that._database = []; that._database = [];
} }
while (i < that._database.length) { while (i < that._database.length) {
meta = that._database[i]; meta = that._database[i];
if (type(meta) === "Object" && if (typeof meta === 'object' &&
Object.getPrototypeOf(meta || []) === Object.prototype &&
typeof meta._id === "string" && meta._id !== "" && typeof meta._id === "string" && meta._id !== "" &&
!that._location[meta._id]) { !that._location[meta._id]) {
that._location[meta._id] = i; that._location[meta._id] = i;
...@@ -461,10 +359,10 @@ ...@@ -461,10 +359,10 @@
/** /**
* Returns the serialized version of this object (not cloned) * Returns the serialized version of this object (not cloned)
* *
* @method serialized * @method toJSON
* @return {Object} The serialized version * @return {Object} The serialized version
*/ */
that.serialized = function () { that.toJSON = function () {
return { return {
"indexing": that._indexing, "indexing": that._indexing,
"free": that._free, "free": that._free,
...@@ -481,502 +379,480 @@ ...@@ -481,502 +379,480 @@
} }
/** /**
* The JIO index storage constructor * Return the similarity percentage (1 >= p >= 0) between two index lists.
*
* @param {Array} list_a An index list
* @param {Array} list_b Another index list
* @return {Number} The similarity percentage
*/ */
function indexStorage(spec, my) { function similarityPercentage(list_a, list_b) {
var that, priv = {}; var ai, bi, count = 0;
for (ai = 0; ai < list_a.length; ai += 1) {
for (bi = 0; bi < list_b.length; bi += 1) {
if (list_a[ai] === list_b[bi]) {
count += 1;
break;
}
}
}
return count / (list_a.length > list_b.length ?
list_a.length : list_b.length);
}
that = my.basicStorage(spec, my); /**
* The JIO index storage constructor
*
* @class IndexStorage
* @constructor
*/
function IndexStorage(spec) {
var i;
if (!Array.isArray(spec.indices)) {
throw new TypeError("IndexStorage 'indices' must be an array of " +
"objects.");
}
this._indices = spec.indices;
if (typeof spec.sub_storage !== 'object' ||
Object.getPrototypeOf(spec.sub_storage || []) !== Object.prototype) {
throw new TypeError("IndexStorage 'sub_storage' must be a storage " +
"description.");
}
// check indices IDs
for (i = 0; i < this._indices.length; i += 1) {
if (typeof this._indices[i].id !== "string" ||
this._indices[i].id === "") {
throw new TypeError("IndexStorage " +
"'indices[x].id' must be a non empty string");
}
if (!Array.isArray(this._indices[i].index)) {
throw new TypeError("IndexStorage " +
"'indices[x].index' must be a string array");
}
}
this._sub_storage = spec.sub_storage;
}
priv.indices = spec.indices; /**
priv.sub_storage = spec.sub_storage; * Select the good index to use according to a select list.
*
* @method selectIndex
* @param {Array} select_list An array of strings
* @return {Number} The index index
*/
IndexStorage.prototype.selectIndex = function (select_list) {
var i, tmp, selector = {"index": 0, "similarity": 0};
for (i = 0; i < this._indices.length; i += 1) {
tmp = similarityPercentage(select_list, this._indices[i].index);
if (tmp > selector.similarity) {
selector.index = i;
selector.similarity = tmp;
}
}
return selector.index;
};
// Overrides IndexStorage.prototype.getIndexDatabase = function (command, index) {
index = this._indices[index];
function makeNewIndex() {
return new JSONIndex({
"_id": index.id,
"_attachment": index.attachment || "body",
"indexing": index.index
});
}
return command.storage(
index.sub_storage || this._sub_storage
).getAttachment({
"_id": index.id,
"_attachment": index.attachment || "body"
}).then(function (response) {
return jIO.util.readBlobAsText(response.data);
}).then(function (e) {
try {
e = JSON.parse(e.target.result);
e._id = index.id;
e._attachment = index.attachment || "body";
} catch (e1) {
return makeNewIndex();
}
return new JSONIndex(e);
}, function (err) {
if (err.status === 404) {
return makeNewIndex();
// go back to fulfillment channel
}
throw err;
// propagate err
});
};
that.specToStore = function () { IndexStorage.prototype.getIndexDatabases = function (command) {
return { var i, promises = [];
"indices": priv.indices, for (i = 0; i < this._indices.length; i += 1) {
"sub_storage": priv.sub_storage promises[promises.length] = this.getIndexDatabase(command, i);
}; }
}; return RSVP.all(promises);
};
/** IndexStorage.prototype.storeIndexDatabase = function (command, database,
* Return the similarity percentage (1 >= p >= 0) between two index lists. index) {
* var that = this;
* @method similarityPercentage index = this._indices[index];
* @param {Array} list_a An index list function putAttachment() {
* @param {Array} list_b Another index list return command.storage(
* @return {Number} The similarity percentage index.sub_storage || that._sub_storage
*/ ).putAttachment({
priv.similarityPercentage = function (list_a, list_b) { "_id": index.id,
var ai, bi, count = 0; "_attachment": index.attachment || "body",
for (ai = 0; ai < list_a.length; ai += 1) { "_data": JSON.stringify(database),
for (bi = 0; bi < list_b.length; bi += 1) { "_content_type": "application/json"
if (list_a[ai] === list_b[bi]) { });
count += 1; }
} function createDatabaseAndPutAttachmentIfPossible(err) {
} if (err.status === 404) {
return command.storage(
index.sub_storage || that._sub_storage
).post({
"_id": index.id
// XXX add metadata to document if necessary
}).then(putAttachment, null, function () {
throw null; // stop post progress propagation
});
} }
return count / (list_a.length > list_b.length ? throw err;
list_a.length : list_b.length); }
}; return putAttachment().
then(null, createDatabaseAndPutAttachmentIfPossible);
};
/** IndexStorage.prototype.storeIndexDatabases = function (command, databases) {
* Select the good index to use according to a select list. var i, promises = [];
* for (i = 0; i < this._indices.length; i += 1) {
* @method selectIndex if (databases[i] !== undefined) {
* @param {Array} select_list An array of strings promises[promises.length] =
* @return {Number} The index index this.storeIndexDatabase(command, databases[i], i);
*/
priv.selectIndex = function (select_list) {
var i, tmp, selector = {"index": 0, "similarity": 0};
for (i = 0; i < priv.indices.length; i += 1) {
tmp = priv.similarityPercentage(select_list,
priv.indices[i].index);
if (tmp > selector.similarity) {
selector.index = i;
selector.similarity = tmp;
}
} }
return selector.index; }
}; return RSVP.all(promises);
};
/**
* Get a database
*
* @method getIndexDatabase
* @param {Object} option The command option
* @param {Number} number The location in priv.indices
* @param {Function} callback The callback
*/
priv.getIndexDatabase = function (option, number, callback) {
that.addJob(
"getAttachment",
priv.indices[number].sub_storage || priv.sub_storage,
{
"_id": priv.indices[number].id,
"_attachment": priv.indices[number].attachment || "body"
},
option,
function (response) {
try {
response = JSON.parse(response);
response._id = priv.indices[number].id;
response._attachment = priv.indices[number].attachment || "body";
callback(new JSONIndex(response));
} catch (e) {
return that.error(generateErrorObject(
e.message,
"Repair is necessary",
"corrupt"
));
}
},
function (err) {
if (err.status === 404) {
callback(new JSONIndex({
"_id": priv.indices[number].id,
"_attachment": priv.indices[number].attachment || "body",
"indexing": priv.indices[number].index
}));
return;
}
err.message = "Unable to get index database.";
that.error(err);
}
);
};
/** /**
* Gets a list containing all the databases set in the storage description. * Generic method for 'post', 'put', 'get' and 'remove'. It delegates the
* * command to the sub storage and update the databases.
* @method getIndexDatabaseList *
* @param {Object} option The command option * @method genericCommand
* @param {Function} callback The result callback(database_list) * @param {String} method The method to use
*/ * @param {Object} command The JIO command
priv.getIndexDatabaseList = function (option, callback) { * @param {Object} metadata The metadata to post
var i, count = 0, callbacks = {}, response_list = []; * @param {Object} option The command option
callbacks.error = function (index) { */
return function (err) { IndexStorage.prototype.genericCommand = function (method, command,
if (err.status === 404) { metadata, option) {
response_list[index] = new JSONIndex({ var that = this, generic_response;
"_id": priv.indices[index].id, function updateAndStoreIndexDatabases(responses) {
"_attachment": priv.indices[index].attachment || "body", var i, database_list = responses[0];
"indexing": priv.indices[index].index generic_response = responses[1];
}); if (method === 'get') {
count += 1; jIO.util.dictUpdate(metadata, generic_response.data);
if (count === priv.indices.length) {
callback(response_list);
}
return;
}
err.message = "Unable to get index database.";
that.error(err);
};
};
callbacks.success = function (index) {
return function (response) {
try {
response = JSON.parse(response);
response._id = priv.indices[index].id;
response._attachment = priv.indices[index].attachment || "body";
response_list[index] = new JSONIndex(response);
} catch (e) {
return that.error(generateErrorObject(
e.message,
"Repair is necessary",
"corrupt"
));
}
count += 1;
if (count === priv.indices.length) {
callback(response_list);
}
};
};
for (i = 0; i < priv.indices.length; i += 1) {
that.addJob(
"getAttachment",
priv.indices[i].sub_storage || priv.sub_storage,
{
"_id": priv.indices[i].id,
"_attachment": priv.indices[i].attachment || "body"
},
option,
callbacks.success(i),
callbacks.error(i)
);
} }
}; metadata._id = generic_response.id;
if (method === 'remove') {
/** for (i = 0; i < database_list.length; i += 1) {
* Saves all the databases to the remote(s). database_list[i].remove(metadata);
*
* @method storeIndexDatabaseList
* @param {Array} database_list The database list
* @param {Object} option The command option
* @param {Function} callback The result callback(err, response)
*/
priv.storeIndexDatabaseList = function (database_list, option, callback) {
var i, count = 0, count_max = 0;
function onAttachmentResponse(response) {
count += 1;
if (count === count_max) {
callback({"ok": true});
} }
} } else {
function onAttachmentError(err) { for (i = 0; i < database_list.length; i += 1) {
err.message = "Unable to store index database."; database_list[i].put(metadata);
that.error(err);
}
function putAttachment(i) {
that.addJob(
"putAttachment",
priv.indices[i].sub_storage || priv.sub_storage,
{
"_id": database_list[i]._id,
"_attachment": database_list[i]._attachment,
"_data": JSON.stringify(database_list[i].serialized()),
"_mimetype": "application/json"
},
option,
onAttachmentResponse,
onAttachmentError
);
}
function post(i) {
var doc = priv.indices[i].metadata || {};
doc._id = database_list[i]._id;
that.addJob(
"post", // with id
priv.indices[i].sub_storage || priv.sub_storage,
doc,
option,
function (response) {
putAttachment(i);
},
function (err) {
if (err.status === 409) {
return putAttachment(i);
}
err.message = "Unable to store index database.";
that.error(err);
}
);
}
for (i = 0; i < priv.indices.length; i += 1) {
if (database_list[i] !== undefined) {
count_max += 1;
post(i);
} }
} }
}; return that.storeIndexDatabases(command, database_list);
}
/** function allProgress(progress) {
* A generic request method which delegates the request to the sub storage. if (progress.index === 1) {
* On response, it will index the document from the request and update all progress.value.percentage *= 0.7; // 0 to 70%
* the databases. command.notify(progress.value);
* }
* @method genericRequest throw null; // stop propagation
* @param {Command} command The JIO command }
* @param {Function} method The request method
*/
priv.genericRequest = function (command, method) {
var doc = command.cloneDoc(), option = command.cloneOption();
that.addJob(
method,
priv.sub_storage,
doc,
option,
function (response) {
switch (method) {
case "post":
case "put":
case "remove":
doc._id = response.id;
priv.getIndexDatabaseList(option, function (database_list) {
var i;
switch (method) {
case "post":
case "put":
for (i = 0; i < database_list.length; i += 1) {
database_list[i].put(doc);
}
break;
case "remove":
for (i = 0; i < database_list.length; i += 1) {
database_list[i].remove(doc);
}
break;
default:
break;
}
priv.storeIndexDatabaseList(database_list, option, function () {
that.success({"ok": true, "id": doc._id});
});
});
break;
default:
that.success(response);
break;
}
},
function (err) {
return that.error(err);
}
);
};
/** function success() {
* Post the document metadata and update the index command.success(generic_response);
* @method post }
* @param {object} command The JIO command
*/
that.post = function (command) {
priv.genericRequest(command, 'post');
};
/** function storeProgress(progress) {
* Update the document metadata and update the index progress.percentage = (0.3 * progress.percentage) + 70; // 70 to 100%
* @method put command.notify(progress);
* @param {object} command The JIO command }
*/
that.put = function (command) {
priv.genericRequest(command, 'put');
};
/** RSVP.all([
* Add an attachment to a document (no index modification) this.getIndexDatabases(command),
* @method putAttachment command.storage(this._sub_storage)[method](metadata, option)
* @param {object} command The JIO command ]).then(updateAndStoreIndexDatabases, null, allProgress).
*/ then(success, command.error, storeProgress);
that.putAttachment = function (command) { };
priv.genericRequest(command, 'putAttachment');
};
/** /**
* Get the document metadata * Post the document metadata and update the index
* @method get *
* @param {object} command The JIO command * @method post
*/ * @param {Object} command The JIO command
that.get = function (command) { * @param {Object} metadata The metadata to post
priv.genericRequest(command, 'get'); * @param {Object} option The command option
}; */
IndexStorage.prototype.post = function (command, metadata, option) {
this.genericCommand('post', command, metadata, option);
};
/** /**
* Get the attachment. * Update the document metadata and update the index
* @method getAttachment *
* @param {object} command The JIO command * @method put
*/ * @param {Object} command The JIO command
that.getAttachment = function (command) { * @param {Object} metadata The metadata to put
priv.genericRequest(command, 'getAttachment'); * @param {Object} option The command option
}; */
IndexStorage.prototype.put = function (command, metadata, option) {
this.genericCommand('put', command, metadata, option);
};
/** /**
* Remove document - removing documents updates index!. * Add an attachment to a document (no index modification)
* @method remove *
* @param {object} command The JIO command * @method putAttachment
*/ * @param {Object} command The JIO command
that.remove = function (command) { * @param {Object} param The command parameters
priv.genericRequest(command, 'remove'); * @param {Object} option The command option
}; */
IndexStorage.prototype.putAttachment = function (command, param, option) {
command.storage(this._sub_storage).putAttachment(param, option).
then(command.success, command.error, command.notify);
};
/** /**
* Remove attachment * Get the document metadata
* @method removeAttachment *
* @param {object} command The JIO command * @method get
*/ * @param {Object} command The JIO command
that.removeAttachment = function (command) { * @param {Object} param The command parameters
priv.genericRequest(command, 'removeAttachment'); * @param {Object} option The command option
}; */
IndexStorage.prototype.get = function (command, param, option) {
this.genericCommand('get', command, param, option);
};
/** /**
* Gets a document list from the substorage * Get the attachment.
* Options: *
* - {boolean} include_docs Also retrieve the actual document content. * @method getAttachment
* @method allDocs * @param {Object} command The JIO command
* @param {object} command The JIO command * @param {Object} param The command parameters
*/ * @param {Object} option The command option
that.allDocs = function (command) { */
var option = command.cloneOption(), IndexStorage.prototype.getAttachment = function (command, param, option) {
index = priv.selectIndex(option.select_list || []); command.storage(this._sub_storage).getAttachment(param, option).
// Include docs option is ignored, if you want to get all the document, then(command.success, command.error, command.notify);
// don't use index storage! };
option.select_list = option.select_list || []; /**
option.select_list.push("_id"); * Remove document - removing documents updates index!.
priv.getIndexDatabase(option, index, function (db) { *
var i, id; * @method remove
db = db._database; * @param {Object} command The JIO command
complex_queries.QueryFactory.create(option.query || ''). * @param {Object} param The command parameters
exec(db, option); * @param {Object} option The command option
for (i = 0; i < db.length; i += 1) { */
id = db[i]._id; IndexStorage.prototype.remove = function (command, param, option) {
delete db[i]._id; this.genericCommand('remove', command, param, option);
db[i] = { };
"id": id,
"key": id,
"value": db[i],
};
}
that.success({"total_rows": db.length, "rows": db});
});
};
that.check = function (command) { /**
that.repair(command, true); * Remove attachment
}; *
* @method removeAttachment
* @param {Object} command The JIO command
* @param {Object} param The command parameters
* @param {Object} option The command option
*/
IndexStorage.prototype.removeAttachment = function (command, param, option) {
command.storage(this._sub_storage).removeAttachment(param, option).
then(command.success, command.error, command.notify);
};
priv.repairIndexDatabase = function (command, index, just_check) { /**
var i, option = command.cloneOption(); * Gets a document list from the substorage
that.addJob( * Options:
'allDocs', * - {boolean} include_docs Also retrieve the actual document content.
priv.sub_storage, * @method allDocs
{}, * @param {object} command The JIO command
{'include_docs': true}, */
function (response) { IndexStorage.prototype.allDocs = function (command, param, option) { // XXX
var db_list = [], db = new JSONIndex({ /*jslint unparam: true */
"_id": command.getDocId(), var index = this.selectIndex(option.select_list || []), delete_id;
"_attachment": priv.indices[index].attachment || "body",
"indexing": priv.indices[index].index
});
for (i = 0; i < response.rows.length; i += 1) {
db.put(response.rows[i].doc);
}
db_list[index] = db;
if (just_check) {
priv.getIndexDatabase(option, index, function (current_db) {
if (db.equals(current_db)) {
return that.success({"ok": true, "id": command.getDocId()});
}
return that.error(generateErrorObject(
"Different Index",
"Check failed",
"corrupt index database"
));
});
} else {
priv.storeIndexDatabaseList(db_list, {}, function () {
that.success({"ok": true, "id": command.getDocId()});
});
}
},
function (err) {
err.message = "Unable to repair the index database";
that.error(err);
}
);
};
priv.repairDocument = function (command, just_check) { // Include docs option is ignored, if you want to get all the document,
var i, option = command.cloneOption(); // don't use index storage!
that.addJob(
"get",
priv.sub_storage,
command.cloneDoc(),
{},
function (response) {
response._id = command.getDocId();
priv.getIndexDatabaseList(option, function (database_list) {
if (just_check) {
for (i = 0; i < database_list.length; i += 1) {
try {
database_list[i].checkDocument(response);
} catch (e) {
return that.error(generateErrorObject(
e.message,
"Check failed",
"corrupt index database"
));
}
}
that.success({"_id": command.getDocId(), "ok": true});
} else {
for (i = 0; i < database_list.length; i += 1) {
database_list[i].put(response);
}
priv.storeIndexDatabaseList(database_list, option, function () {
that.success({"ok": true, "id": command.getDocId()});
});
}
});
},
function (err) {
err.message = "Unable to repair document";
return that.error(err);
}
);
};
that.repair = function (command, just_check) { option.select_list = (
var database_index = -1, i; Array.isArray(option.select_list) ? option.select_list : []
for (i = 0; i < priv.indices.length; i += 1) { );
if (priv.indices[i].id === command.getDocId()) { if (option.select_list.indexOf("_id") === -1) {
database_index = i; option.select_list.push("_id");
break; delete_id = true;
}
this.getIndexDatabase(command, index).then(function (db) {
var i, id;
db = db._database;
complex_queries.QueryFactory.create(option.query || '').
exec(db, option);
for (i = 0; i < db.length; i += 1) {
id = db[i]._id;
if (delete_id) {
delete db[i]._id;
} }
db[i] = {
"id": id,
"value": db[i]
};
} }
that.addJob( command.success(200, {"data": {"total_rows": db.length, "rows": db}});
"repair", }, function (err) {
priv.sub_storage, if (err.status === 404) {
command.cloneDoc(), return command.success(200, {"data": {"total_rows": 0, "rows": []}});
command.cloneOption(), }
function (response) { command.error(err);
if (database_index !== -1) { });
priv.repairIndexDatabase(command, database_index, just_check); };
} else {
priv.repairDocument(command, just_check);
}
},
function (err) {
err.message = "Could not repair sub storage";
that.error(err);
}
);
};
return that; // IndexStorage.prototype.check = function (command, param, option) { // XXX
} // this.repair(command, true, param, option);
// };
// IndexStorage.prototype.repairIndexDatabase = function (
// command,
// index,
// just_check,
// param,
// option
// ) { // XXX
// var i, that = this;
// command.storage(this._sub_storage).allDocs({'include_docs': true}).then(
// function (response) {
// var db_list = [], db = new JSONIndex({
// "_id": param._id,
// "_attachment": that._indices[index].attachment || "body",
// "indexing": that._indices[index].index
// });
// for (i = 0; i < response.rows.length; i += 1) {
// db.put(response.rows[i].doc);
// }
// db_list[index] = db;
// if (just_check) {
// this.getIndexDatabase(command, option, index, function (current_db) {
// if (db.equals(current_db)) {
// return command.success({"ok": true, "id": param._id});
// }
// return command.error(
// "conflict",
// "corrupted",
// "Database is not up to date"
// );
// });
// } else {
// that.storeIndexDatabaseList(command, db_list, {}, function () {
// command.success({"ok": true, "id": param._id});
// });
// }
// },
// function (err) {
// err.message = "Unable to repair the index database";
// command.error(err);
// }
// );
// };
// IndexStorage.prototype.repairDocument = function (
// command,
// just_check,
// param,
// option
// ) { // XXX
// var i, that = this;
// command.storage(this._sub_storage).get(param, {}).then(
// function (response) {
// response._id = param._id;
// that.getIndexDatabaseList(command, option, function (database_list) {
// if (just_check) {
// for (i = 0; i < database_list.length; i += 1) {
// try {
// database_list[i].checkDocument(response);
// } catch (e) {
// return command.error(
// "conflict",
// e.message,
// "Corrupt index database"
// );
// }
// }
// command.success({"_id": param._id, "ok": true});
// } else {
// for (i = 0; i < database_list.length; i += 1) {
// database_list[i].put(response);
// }
// that.storeIndexDatabaseList(
// command,
// database_list,
// option,
// function () {
// command.success({"ok": true, "id": param._id});
// }
// );
// }
// });
// },
// function (err) {
// err.message = "Unable to repair document";
// return command.error(err);
// }
// );
// };
// IndexStorage.prototype.repair = function (command, just_check, param,
// option) { // XXX
// var database_index = -1, i, that = this;
// for (i = 0; i < this._indices.length; i += 1) {
// if (this._indices[i].id === param._id) {
// database_index = i;
// break;
// }
// }
// command.storage(this._sub_storage).repair(param, option).then(
// function () {
// if (database_index !== -1) {
// that.repairIndexDatabase(
// command,
// database_index,
// just_check,
// param,
// option
// );
// } else {
// that.repairDocument(command, just_check, param, option);
// }
// },
// function (err) {
// err.message = "Could not repair sub storage";
// command.error(err);
// }
// );
// };
jIO.addStorage("indexed", IndexStorage);
exports.createDescription = function () {
// XXX
return;
};
jIO.addStorageType("indexed", indexStorage);
})); }));
/*jslint indent: 2, maxlen: 80, nomen: true */ /*jslint indent: 2, maxlen: 80, nomen: true */
/*global define, jIO, jio_tests, test, ok, deepEqual, sinon */ /*global define, module, test_util, RSVP, jIO, local_storage, test, ok,
deepEqual, stop, start */
// define([module_name], [dependencies], module); // define([module_name], [dependencies], module);
(function (dependencies, module) { (function (dependencies, module) {
...@@ -7,1108 +8,621 @@ ...@@ -7,1108 +8,621 @@
if (typeof define === 'function' && define.amd) { if (typeof define === 'function' && define.amd) {
return define(dependencies, module); return define(dependencies, module);
} }
module(jIO, jio_tests); module(test_util, RSVP, jIO, local_storage);
}(['jio', 'jio_tests', 'localstorage', 'indexstorage'], function (jIO, util) { }([
'test_util',
'rsvp',
'jio',
'localstorage',
'indexstorage'
], function (util, RSVP, jIO, local_storage) {
"use strict"; "use strict";
function generateTools() { function success(promise) {
return { return new RSVP.Promise(function (resolve, reject, notify) {
clock: sinon.useFakeTimers(), /*jslint unparam: true*/
spy: util.ospy, promise.then(resolve, resolve, notify);
tick: util.otick }, function () {
}; promise.cancel();
}
module("IndexStorage");
test("Post", function () {
var o = generateTools();
o.jio = jIO.newJio({
"type": "indexed",
"indices": [
{"id": "A", "index": ["title"]},
{"id": "B", "index": ["title", "year"]}
],
"sub_storage": {
"type": "local",
"username": "ipost",
"application_name": "ipost"
}
}); });
}
o.getAttachmentCallback = function (err, response) { /**
if (response) { * sequence(thens): Promise
try { *
response = JSON.parse(response); * Executes a sequence of *then* callbacks. It acts like
} catch (e) { * `smth().then(callback).then(callback)...`. The first callback is called
response = "PARSE ERROR " + response; * with no parameter.
*
* Elements of `thens` array can be a function or an array contaning at most
* three *then* callbacks: *onFulfilled*, *onRejected*, *onNotified*.
*
* When `cancel()` is executed, each then promises are cancelled at the same
* time.
*
* @param {Array} thens An array of *then* callbacks
* @return {Promise} A new promise
*/
function sequence(thens) {
var promises = [];
return new RSVP.Promise(function (resolve, reject, notify) {
var i;
promises[0] = new RSVP.Promise(function (resolve) {
resolve();
});
for (i = 0; i < thens.length; i += 1) {
if (Array.isArray(thens[i])) {
promises[i + 1] = promises[i].
then(thens[i][0], thens[i][1], thens[i][2]);
} else {
promises[i + 1] = promises[i].then(thens[i]);
} }
} }
o.f(err, response); promises[i].then(resolve, reject, notify);
}; }, function () {
var i;
// post without id for (i = 0; i < promises.length; i += 1) {
o.spy(o, "jobstatus", "done", "Post without id"); promises[i].cancel();
o.jio.post({}, function (err, response) {
o.id = (response || {}).id;
o.f(err, response);
});
o.tick(o);
// post non empty document
o.doc = {"_id": "some_id", "title": "My Title",
"year": 2000, "hey": "def"};
o.spy(o, "value", {"ok": true, "id": "some_id"}, "Post document");
o.jio.post(o.doc, o.f);
o.tick(o);
// check document
o.fakeIndexA = {
"indexing": ["title"],
"free": [],
"location": {
"some_id": 0
},
"database": [
{"_id": "some_id", "title": "My Title"}
]
};
o.spy(o, "value", o.fakeIndexA, "Check index file");
o.jio.getAttachment({
"_id": "A",
"_attachment": "body"
}, o.getAttachmentCallback);
o.tick(o);
o.fakeIndexB = {
"indexing": ["title", "year"],
"free": [],
"location": {
"some_id": 0
},
"database": [
{"_id": "some_id", "title": "My Title", "year": 2000}
]
};
o.spy(o, "value", o.fakeIndexB, "Check index file");
o.jio.getAttachment({
"_id": "B",
"_attachment": "body"
}, o.getAttachmentCallback);
o.tick(o);
// post with escapable characters
o.doc = {
"_id": "other_id",
"title": "myPost2",
"findMeA": "keyword_*§$%&/()=?",
"findMeB": "keyword_|ð@ł¶đæðſæðæſ³"
};
o.spy(o, "value", {"ok": true, "id": "other_id"},
"Post with escapable characters");
o.jio.post(o.doc, o.f);
o.tick(o);
// post and document already exists
o.doc = {
"_id": "some_id",
"title": "myPost3",
"findMeA": "keyword_ghi",
"findMeB": "keyword_jkl"
};
o.spy(o, "status", 409, "Post and document already exists");
o.jio.post(o.doc, o.f);
o.tick(o);
util.closeAndcleanUpJio(o.jio);
});
test("Put", function () {
var o = generateTools();
o.jio = jIO.newJio({
"type": "indexed",
"indices": [
{"id": "A", "index": ["author"]},
{"id": "B", "index": ["year"]}
],
"sub_storage": {
"type": "local",
"username": "iput",
"application_name": "iput"
} }
}); });
}
o.getAttachmentCallback = function (err, response) { module("IndexStorage");
if (response) {
try {
response = JSON.parse(response);
} catch (e) {
response = "PARSE ERROR " + response;
}
}
o.f(err, response);
};
// put without id
// error 20 -> document id required
o.spy(o, "status", 20, "Put without id");
o.jio.put({}, o.f);
o.tick(o);
// put non empty document
o.doc = {"_id": "put1", "title": "myPut1", "author": "John Doe"};
o.spy(o, "value", {"ok": true, "id": "put1"}, "Put-create document");
o.jio.put(o.doc, o.f);
o.tick(o);
// check index file
o.fakeIndexA = {
"indexing": ["author"],
"free": [],
"location": {
"put1": 0
},
"database": [{"_id": "put1", "author": "John Doe"}]
};
o.spy(o, "value", o.fakeIndexA, "Check index file");
o.jio.getAttachment({
"_id": "A",
"_attachment": "body"
}, o.getAttachmentCallback);
o.tick(o);
o.fakeIndexB = {
"indexing": ["year"],
"free": [],
"location": {},
"database": []
};
o.spy(o, "value", o.fakeIndexB, "Check index file");
o.jio.getAttachment({
"_id": "B",
"_attachment": "body"
}, o.getAttachmentCallback);
o.tick(o);
// modify document - modify keyword on index!
o.doc = {"_id": "put1", "title": "myPuttter1", "author": "Jane Doe"};
o.spy(o, "value", {"ok": true, "id": "put1"}, "Modify existing document");
o.jio.put(o.doc, o.f);
o.tick(o);
// check index file
o.fakeIndexA.database[0].author = "Jane Doe";
o.spy(o, "value", o.fakeIndexA, "Check index file");
o.jio.getAttachment({
"_id": "A",
"_attachment": "body"
}, o.getAttachmentCallback);
o.tick(o);
// add new document with same keyword!
o.doc = {"_id": "new_doc", "title": "myPut2", "author": "Jane Doe"};
o.spy(o, "value", {"ok": true, "id": "new_doc"},
"Add new document with same keyword");
o.jio.put(o.doc, o.f);
o.tick(o);
// check index file
o.fakeIndexA.location.new_doc = 1;
o.fakeIndexA.database.push({"_id": "new_doc", "author": "Jane Doe"});
o.spy(o, "value", o.fakeIndexA, "Check index file");
o.jio.getAttachment({
"_id": "A",
"_attachment": "body"
}, o.getAttachmentCallback);
o.tick(o);
// add second keyword to index file
o.doc = {"_id": "put1", "title": "myPut2", "author": "Jane Doe",
"year": "1912"};
o.spy(o, "value", {"ok": true, "id": "put1"},
"add second keyword to index file");
o.jio.put(o.doc, o.f);
o.tick(o);
// check index file
o.spy(o, "value", o.fakeIndexA, "Check index file");
o.jio.getAttachment({
"_id": "A",
"_attachment": "body"
}, o.getAttachmentCallback);
o.tick(o);
o.fakeIndexB.location.put1 = 0;
o.fakeIndexB.database.push({"_id": "put1", "year": "1912"});
o.spy(o, "value", o.fakeIndexB, "Check index file");
o.jio.getAttachment({
"_id": "B",
"_attachment": "body"
}, o.getAttachmentCallback);
o.tick(o);
// remove a keyword from an existing document
o.doc = {"_id": "new_doc", "title": "myPut2"};
o.spy(o, "value", {"ok": true, "id": "new_doc"},
"Remove keyword from existing document");
o.jio.put(o.doc, o.f);
o.tick(o);
// check index file
delete o.fakeIndexA.location.new_doc;
o.fakeIndexA.database[1] = null;
o.fakeIndexA.free.push(1);
o.spy(o, "value", o.fakeIndexA, "Check index file");
o.jio.getAttachment({
"_id": "A",
"_attachment": "body"
}, o.getAttachmentCallback);
o.tick(o);
util.closeAndcleanUpJio(o.jio);
});
test("Check & Repair", function () { test("Scenario", function () {
var o = generateTools(), i;
o.jio = jIO.newJio({ var LOCAL_STORAGE_SPEC = local_storage.createDescription(
'indexstorage tests',
'scenario',
'memory'
), INDEX_STORAGE_SPEC = {
"type": "indexed", "type": "indexed",
"indices": [ "indices": [
{"id": "A", "index": ["director"]}, {"id": "A", "index": ["contributor"]},
{"id": "B", "index": ["year"]} {"id": "B", "index": ["author"]},
{"id": "C", "index": ["title"]},
{"id": "D", "index": ["title", "year"]}
], ],
"sub_storage": { "sub_storage": LOCAL_STORAGE_SPEC
"type": "local", }, option = {"workspace": {}}, shared = {}, jio_index, jio_local;
"username": "indexstoragerepair"
}
});
o.getAttachmentCallback = function (err, response) { jio_index = jIO.createJIO(INDEX_STORAGE_SPEC, option);
if (response) { jio_local = jIO.createJIO(LOCAL_STORAGE_SPEC, option);
try {
response = JSON.parse(response);
} catch (e) {
response = "PARSE ERROR " + response;
}
delete response.location;
response.database.sort(function (a, b) {
return a._id < b._id ? -1 : a._id > b._id ? 1 : 0;
});
}
o.f(err, response);
};
o.fakeIndexA = {
"indexing": ["director"],
"free": [],
"database": []
};
o.fakeIndexB = {
"indexing": ["year"],
"free": [],
"database": []
};
for (i = 0; i < 10; i += 1) {
o.jio.put({
"_id": "id" + i,
"director": "D" + i,
"year": i,
"title": "T" + i
});
o.tmp = o.fakeIndexA.free.pop() || o.fakeIndexA.database.length;
o.fakeIndexA.database[o.tmp] = {"_id": "id" + i, "director": "D" + i};
o.tmp = o.fakeIndexB.free.pop() || o.fakeIndexB.database.length; function postNewDocument() {
o.fakeIndexB.database[o.tmp] = {"_id": "id" + i, "year": i}; return jio_index.post({"title": "Unique ID"});
} }
o.clock.tick(5000);
o.spy(o, "status", 40, "Check database");
o.jio.check({"_id": "A"}, o.f);
o.tick(o);
o.spy(o, "status", 40, "Check database");
o.jio.check({"_id": "B"}, o.f);
o.tick(o);
o.spy(o, "value", {"id": "A", "ok": true}, "Repair database");
o.jio.repair({"_id": "A"}, o.f);
o.tick(o);
o.spy(o, "value", {"id": "B", "ok": true}, "Repair database");
o.jio.repair({"_id": "B"}, o.f);
o.tick(o);
o.spy(o, "value", {"id": "A", "ok": true}, "Check database again");
o.jio.check({"_id": "A"}, o.f);
o.tick(o);
o.spy(o, "value", {"id": "B", "ok": true}, "Check database again");
o.jio.check({"_id": "B"}, o.f);
o.tick(o);
// check index file
o.spy(o, "value", o.fakeIndexA, "Manually check index file");
o.jio.getAttachment({
"_id": "A",
"_attachment": "body"
}, o.getAttachmentCallback);
o.tick(o);
o.spy(o, "value", o.fakeIndexB, "Manually check index file");
o.jio.getAttachment({
"_id": "B",
"_attachment": "body"
}, o.getAttachmentCallback);
o.tick(o);
o.jio2 = jIO.newJio({"type": "local", "username": "indexstoragerepair"});
o.jio2.put({"_id": "blah", "title": "t", "year": "y", "director": "d"});
o.clock.tick(1000);
util.closeAndcleanUpJio(o.jio2);
o.fakeIndexA.database.unshift({"_id": "blah", "director": "d"});
o.fakeIndexB.database.unshift({"_id": "blah", "year": "y"});
o.spy(o, "status", 40, "Check Document");
o.jio.check({"_id": "blah"}, o.f);
o.tick(o);
o.spy(o, "value", {"id": "blah", "ok": true}, "Repair Document");
o.jio.repair({"_id": "blah"}, o.f);
o.tick(o);
o.spy(o, "value", {"id": "blah", "ok": true}, "Check Document again");
o.jio.repair({"_id": "blah"}, o.f);
o.tick(o);
// check index file
o.spy(o, "value", o.fakeIndexA, "Manually check index file");
o.jio.getAttachment({
"_id": "A",
"_attachment": "body"
}, o.getAttachmentCallback);
o.tick(o);
o.spy(o, "value", o.fakeIndexB, "Manually check index file");
o.jio.getAttachment({
"_id": "B",
"_attachment": "body"
}, o.getAttachmentCallback);
o.tick(o);
util.closeAndcleanUpJio(o.jio);
});
test("PutAttachment", function () { function postNewDocumentTest(answer) {
var uuid = answer.id;
// not sure these need to be run, because the index does not change answer.id = "<uuid>";
// and only small modifications have been made to handle putAttachment deepEqual(answer, {
// tests are from localStorage putAttachment "id": "<uuid>",
var o = generateTools(); "method": "post",
"result": "success",
o.jio = jIO.newJio({ "status": 201,
"type": "indexed", "statusText": "Created"
"indices": [ }, "Post a new document");
{"id": "A", "index": ["author"]}, ok(util.isUuid(uuid), "New document id should look like " +
{"id": "B", "index": ["year"]} "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx : " + uuid);
], shared.created_document_id = uuid;
"sub_storage": { }
"type": "local",
"username": "iputatt",
"application_name": "iputatt"
}
});
// putAttachment without doc id
// error 20 -> document id required
o.spy(o, "status", 20, "PutAttachment without doc id");
o.jio.putAttachment({}, o.f);
o.tick(o);
// putAttachment without attachment id
// error 22 -> attachment id required
o.spy(o, "status", 22, "PutAttachment without attachment id");
o.jio.putAttachment({"_id": "putattmt1"}, o.f);
o.tick(o);
// putAttachment without document
// error 404 -> not found
o.spy(o, "status", 404, "PutAttachment without document");
o.jio.putAttachment({"_id": "putattmt1", "_attachment": "putattmt2"}, o.f);
o.tick(o);
// putAttachment with document function getCreatedDocument() {
o.doc = {"_id": "putattmt1", "title": "myPutAttmt1"}; return jio_index.get({"_id": shared.created_document_id});
o.spy(o, "value", {"ok": true, "id": "putattmt1"}, }
"Put underlying document");
o.jio.put(o.doc, o.f);
o.tick(o);
o.spy(o, "value", { function getCreatedDocumentTest(answer) {
"ok": true, deepEqual(answer, {
"id": "putattmt1", "data": {
"attachment": "putattmt2" "_id": shared.created_document_id,
}, "PutAttachment with document, without data"); "title": "Unique ID"
o.jio.putAttachment({"_id": "putattmt1", "_attachment": "putattmt2"}, o.f); },
o.tick(o); "id": shared.created_document_id,
"method": "get",
"result": "success",
"status": 200,
"statusText": "Ok"
}, "Get new document");
}
// check document // function postSpecificDocuments() {
deepEqual(util.jsonlocalstorage.getItem( // return success(RSVP.all([
"jio/localstorage/iputatt/iputatt/putattmt1" // jio_index.post({"_id": "b", "title": "Bee", "year": 2013}),
), { // jio_index.post({"_id": "ce", "contributor": "DCee"}),
"_id": "putattmt1", // jio_index.post({"_id": "dee", "format": "text/plain"})
"title": "myPutAttmt1", // ]));
"_attachments": { // }
"putattmt2": {
"length": 0, // function postSpecificDocumentsTest(answers) {
// md5("") // deepEqual(answers[0], {
"digest": "md5-d41d8cd98f00b204e9800998ecf8427e" // "id": "b",
} // "method": "post",
} // "result": "success",
}, "Check document"); // "status": 201,
// "statusText": "Created"
// }, "Post specific document 'b'");
// deepEqual(answers[1], {
// "id": "ce",
// "method": "post",
// "result": "success",
// "status": 201,
// "statusText": "Created"
// }, "Post specific document 'ce'");
// deepEqual(answers[2], {
// "id": "dee",
// "method": "post",
// "result": "success",
// "status": 201,
// "statusText": "Created"
// }, "Post specific document 'dee'");
// }
// XXX the 2 following functions should be replaced by the 2 commented
// previous ones (which don't work yet)
function postSpecificDocuments() {
return sequence([function () {
return jio_index.post({"_id": "b", "title": "Bee", "year": 2013});
}, function () {
return jio_index.post({"_id": "ce", "contributor": "DCee"});
}, function () {
return jio_index.post({"_id": "dee", "format": "text/plain"});
}]);
}
// check attachment function postSpecificDocumentsTest(last_answer) {
deepEqual(util.jsonlocalstorage.getItem( deepEqual(last_answer, {
"jio/localstorage/iputatt/iputatt/putattmt1/putattmt2" "id": "dee",
), "", "Check attachment"); "method": "post",
"result": "success",
"status": 201,
"statusText": "Created"
}, "Post documents: 'b', 'ce', 'dee' (testing 'dee' response only)");
}
// update attachment function listDocumentsFromIndexContributor() {
o.spy(o, "value", return jio_index.allDocs({"select_list": ["contributor"]});
{"ok": true, "id": "putattmt1", "attachment": "putattmt2"}, }
"Update Attachment, with data");
o.jio.putAttachment({
"_id": "putattmt1",
"_attachment": "putattmt2",
"_data": "abc"
}, o.f);
o.tick(o);
// check document function listDocumentsFromIndexContributorTest(answer) {
deepEqual(util.jsonlocalstorage.getItem( if (answer && answer.data && Array.isArray(answer.data.rows)) {
"jio/localstorage/iputatt/iputatt/putattmt1" answer.data.rows.sort(function (a, b) {
), { return a.id.length < b.id.length ? -1 : (
"_id": "putattmt1", a.id.length > b.id.length ? 1 : 0
"title": "myPutAttmt1", );
"_attachments": { });
"putattmt2": {
"length": 3,
// md5("abc")
"digest": "md5-900150983cd24fb0d6963f7d28e17f72"
}
} }
}, "Check document"); deepEqual(answer, {
"data": {
// check attachment "total_rows": 1,
deepEqual(util.jsonlocalstorage.getItem( "rows": [{
"jio/localstorage/iputatt/iputatt/putattmt1/putattmt2" "id": "ce",
), "abc", "Check attachment"); "value": {"contributor": "DCee"}
}]
util.closeAndcleanUpJio(o.jio); },
}); "method": "allDocs",
"result": "success",
test("Get", function () { "status": 200,
"statusText": "Ok"
}, "List 1 document from 'contributor'");
}
// not sure these need to be run, because the index does not change function listDocumentsFromIndexTitleYear() {
// and only small modifications have been made to handle putAttachment return jio_index.allDocs({"select_list": ["year", "title"]});
// tests are from localStorage putAttachment }
var o = generateTools();
o.jio = jIO.newJio({ function listDocumentsFromIndexTitleYearTest(answer) {
"type": "indexed", if (answer && answer.data && Array.isArray(answer.data.rows)) {
"indices": [ answer.data.rows.sort(function (a, b) {
{"id": "A", "index": ["author"]}, return a.id.length < b.id.length ? -1 : (
{"id": "B", "index": ["year"]} a.id.length > b.id.length ? 1 : 0
], );
"sub_storage": { });
"type": "local",
"username": "iget",
"application_name": "iget"
} }
}); deepEqual(answer, {
"data": {
// get inexistent document "total_rows": 2,
o.spy(o, "status", 404, "Get inexistent document"); "rows": [{
o.jio.get({"_id": "get1"}, o.f); "id": "b",
o.tick(o); "value": {"title": "Bee", "year": 2013}
}, {
// get inexistent attachment "id": shared.created_document_id,
o.spy(o, "status", 404, "Get inexistent attachment"); "value": {"title": "Unique ID"}
o.jio.getAttachment({"_id": "get1", "_attachment": "get2"}, o.f); }]
o.tick(o); },
"method": "allDocs",
// adding a document "result": "success",
o.doc_get1 = { "status": 200,
"_id": "get1", "statusText": "Ok"
"title": "myGet1" }, "List 2 documents from 'year' and 'title'");
}; }
util.jsonlocalstorage.setItem(
"jio/localstorage/iget/iget/get1",
o.doc_get1
);
// get document
o.spy(o, "value", o.doc_get1, "Get document");
o.jio.get({"_id": "get1"}, o.f);
o.tick(o);
// get inexistent attachment (document exists) function listDocumentsFromIndexTitle() {
o.spy(o, "status", 404, "Get inexistent attachment (document exists)"); return jio_index.allDocs({"select_list": ["title"]});
o.jio.getAttachment({"_id": "get1", "_attachment": "get2"}, o.f); }
o.tick(o);
// adding an attachment function listDocumentsFromIndexTitleTest(answer) {
o.doc_get1._attachments = { if (answer && answer.data && Array.isArray(answer.data.rows)) {
"get2": { answer.data.rows.sort(function (a, b) {
"length": 2, return a.id.length < b.id.length ? -1 : (
// md5("de") a.id.length > b.id.length ? 1 : 0
"digest": "md5-5f02f0889301fd7be1ac972c11bf3e7d" );
});
} }
}; deepEqual(answer, {
util.jsonlocalstorage.setItem( "data": {
"jio/localstorage/iget/iget/get1", "total_rows": 2,
o.doc_get1 "rows": [{
); "id": "b",
util.jsonlocalstorage.setItem("jio/localstorage/iget/iget/get1/get2", "de"); "value": {"title": "Bee"}
}, {
// get attachment "id": shared.created_document_id,
o.spy(o, "value", "de", "Get attachment"); "value": {"title": "Unique ID"}
o.jio.getAttachment({"_id": "get1", "_attachment": "get2"}, o.f); }]
o.tick(o); },
"method": "allDocs",
util.closeAndcleanUpJio(o.jio); "result": "success",
}); "status": 200,
"statusText": "Ok"
test("Remove", function () { }, "List 2 documents from 'title'");
}
// not sure these need to be run, because the index does not change
// and only small modifications have been made to handle putAttachment
// tests are from localStorage putAttachment
var o = generateTools();
o.jio = jIO.newJio({ function listDocumentsFromIndexAuthor() {
"type": "indexed", return jio_index.allDocs({"select_list": ["author"]});
"indices": [ }
{"id": "A", "index": ["author"]},
{"id": "B", "index": ["year"]}
],
"sub_storage": {
"type": "local",
"username": "irem",
"application_name": "irem"
}
});
o.getAttachmentCallback = function (err, response) { function listDocumentsFromIndexAuthorTest(answer) {
if (response) { if (answer && answer.data && Array.isArray(answer.data.rows)) {
try { answer.data.rows.sort(function (a, b) {
response = JSON.parse(response); return a.id.length < b.id.length ? -1 : (
} catch (e) { a.id.length > b.id.length ? 1 : 0
response = "PARSE ERROR " + response; );
} });
} }
o.f(err, response); deepEqual(answer, {
}; "data": {
"total_rows": 0,
// remove inexistent document "rows": []
o.spy(o, "status", 404, "Remove inexistent document"); },
o.jio.remove({"_id": "remove1"}, o.f); "method": "allDocs",
o.tick(o); "result": "success",
"status": 200,
// remove inexistent document/attachment "statusText": "Ok"
o.spy(o, "status", 404, "Remove inexistent attachment"); }, "List 0 document from 'author'");
o.jio.removeAttachment({"_id": "remove1", "_attachment": "remove2"}, o.f); }
o.tick(o);
// adding a document
o.jio.put({"_id": "remove1", "title": "myRemove1",
"author": "Mr. President", "year": "2525"
});
o.tick(o);
// adding a 2nd document with same keywords
o.jio.put({"_id": "removeAlso", "title": "myRemove2",
"author": "Martin Mustermann", "year": "2525"
});
o.tick(o);
// remove document
o.spy(o, "value", {"ok": true, "id": "remove1"}, "Remove document");
o.jio.remove({"_id": "remove1"}, o.f);
o.tick(o);
// check index
o.fakeIndexA = {
"indexing": ["author"],
"free": [0],
"location": {
"removeAlso": 1
},
"database": [null, {"_id": "removeAlso", "author": "Martin Mustermann"}]
};
o.spy(o, "value", o.fakeIndexA, "Check index file");
o.jio.getAttachment({
"_id": "A",
"_attachment": "body"
}, o.getAttachmentCallback);
o.tick(o);
o.fakeIndexB = {
"indexing": ["year"],
"free": [0],
"location": {
"removeAlso": 1
},
"database": [null, {"_id": "removeAlso", "year": "2525"}]
};
o.spy(o, "value", o.fakeIndexB, "Check index file");
o.jio.getAttachment({
"_id": "B",
"_attachment": "body"
}, o.getAttachmentCallback);
o.tick(o);
// check document
o.spy(o, "status", 404, "Check if document has been removed");
o.jio.get({"_id": "remove1"}, o.f);
o.tick(o);
// adding a new document
o.jio.put({"_id": "remove3",
"title": "myRemove1",
"author": "Mrs Sunshine",
"year": "1234"
});
o.tick(o);
// adding an attachment
o.jio.putAttachment({
"_id": "remove3",
"_attachment": "removeAtt",
"_mimetype": "text/plain",
"_data": "hello"
});
o.tick(o);
// add another attachment
o.jio.putAttachment({
"_id": "remove3",
"_attachment": "removeAtt2",
"_mimetype": "text/plain",
"_data": "hello2"
});
o.tick(o);
// remove attachment
o.spy(o, "value", {"ok": true, "id": "remove3", "attachment": "removeAtt2"},
"Remove one of multiple attachment");
o.jio.removeAttachment({
"_id": "remove3",
"_attachment": "removeAtt2"
}, o.f);
o.tick(o);
// check index
o.fakeIndexA.free = [];
o.fakeIndexA.location.remove3 = 0;
o.fakeIndexA.database[0] = {"_id": "remove3", "author": "Mrs Sunshine"};
o.spy(o, "value", o.fakeIndexA, "Check index file");
o.jio.getAttachment({
"_id": "A",
"_attachment": "body"
}, o.getAttachmentCallback);
o.tick(o);
o.fakeIndexB.free = [];
o.fakeIndexB.location.remove3 = 0;
o.fakeIndexB.database[0] = {"_id": "remove3", "year": "1234"};
o.spy(o, "value", o.fakeIndexB, "Check index file");
o.jio.getAttachment({
"_id": "B",
"_attachment": "body"
}, o.getAttachmentCallback);
o.tick(o);
// remove document and attachment together
o.spy(o, "value", {"ok": true, "id": "remove3"},
"Remove one document and attachment together");
o.jio.remove({"_id": "remove3"}, o.f);
o.tick(o);
// check index
o.fakeIndexA.free = [0];
delete o.fakeIndexA.location.remove3;
o.fakeIndexA.database[0] = null;
o.spy(o, "value", o.fakeIndexA, "Check index file");
o.jio.getAttachment({
"_id": "A",
"_attachment": "body"
}, o.getAttachmentCallback);
o.tick(o);
o.fakeIndexB.free = [0];
delete o.fakeIndexB.location.remove3;
o.fakeIndexB.database[0] = null;
o.spy(o, "value", o.fakeIndexB, "Check index file");
o.jio.getAttachment({
"_id": "B",
"_attachment": "body"
}, o.getAttachmentCallback);
o.tick(o);
// check attachment
o.spy(o, "status", 404, "Check if attachment has been removed");
o.jio.getAttachment({"_id": "remove3", "_attachment": "removeAtt"}, o.f);
o.tick(o);
// check document
o.spy(o, "status", 404, "Check if document has been removed");
o.jio.get({"_id": "remove3"}, o.f);
o.tick(o);
util.closeAndcleanUpJio(o.jio);
});
test("AllDocs", function () {
var o = generateTools();
o.jio = jIO.newJio({ function listDocumentsFromNothing() {
"type": "indexed", return jio_index.allDocs();
"indices": [ }
{"id": "A", "index": ["author"]},
{"id": "B", "index": ["year"]}
],
"sub_storage": {
"type": "local",
"username": "iall",
"application_name": "iall"
}
});
o.getAttachmentCallback = function (err, response) { function listDocumentsFromNothingTest(answer) {
if (response) { if (answer && answer.data && Array.isArray(answer.data.rows)) {
try { answer.data.rows.sort(function (a, b) {
response = JSON.parse(response); return a.id.length < b.id.length ? -1 : (
} catch (e) { a.id.length > b.id.length ? 1 : 0
response = "PARSE ERROR " + response; );
} });
} }
o.f(err, response); deepEqual(answer, {
}; "data": {
"total_rows": 1,
// adding documents "rows": [{
o.all1 = { "_id": "dragon.doc", "id": "ce",
"title": "some title", "author": "Dr. No", "year": "1968" "value": {}
}; }]
o.spy(o, "value", {"ok": true, "id": "dragon.doc"}, "Put 1"); },
o.jio.put(o.all1, o.f); "method": "allDocs",
o.tick(o); "result": "success",
o.all2 = { "status": 200,
"_id": "timemachine", "statusText": "Ok"
"title": "hello world", }, "List 1 document from first index (`allDocs()`)");
"author": "Dr. Who", }
"year": "1968"
};
o.spy(o, "value", {"ok": true, "id": "timemachine"}, "Put 2");
o.jio.put(o.all2, o.f);
o.tick(o);
o.all3 = {
"_id": "rocket.ppt",
"title": "sunshine.",
"author": "Dr. Snuggles",
"year": "1985"
};
o.spy(o, "value", {"ok": true, "id": "rocket.ppt"}, "Put 3");
o.jio.put(o.all3, o.f);
o.tick(o);
o.all4 = {
"_id": "stick.jpg",
"title": "clouds",
"author": "Dr. House",
"year": "2005"
};
o.spy(o, "value", {"ok": true, "id": "stick.jpg"}, "Put 4");
o.jio.put(o.all4, o.f);
o.tick(o);
// check index
o.fakeIndexA = {
"indexing": ["author"],
"free": [],
"location": {
"dragon.doc": 0,
"timemachine": 1,
"rocket.ppt": 2,
"stick.jpg": 3
},
"database": [
{"_id": "dragon.doc", "author": "Dr. No"},
{"_id": "timemachine", "author": "Dr. Who"},
{"_id": "rocket.ppt", "author": "Dr. Snuggles"},
{"_id": "stick.jpg", "author": "Dr. House"}
]
};
o.spy(o, "value", o.fakeIndexA, "Check index file");
o.jio.getAttachment({
"_id": "A",
"_attachment": "body"
}, o.getAttachmentCallback);
o.tick(o);
o.thisShouldBeTheAnswer = {
"rows": [
{"id": "dragon.doc", "key": "dragon.doc", "value": {} },
{"id": "timemachine", "key": "timemachine", "value": {} },
{"id": "rocket.ppt", "key": "rocket.ppt", "value": {} },
{"id": "stick.jpg", "key": "stick.jpg", "value": {} }
],
"total_rows": 4
};
o.spy(o, "value", o.thisShouldBeTheAnswer, "allDocs (served by index)");
o.jio.allDocs(o.f);
o.tick(o);
util.closeAndcleanUpJio(o.jio);
});
test("AllDocs Complex Queries", function () {
var o = generateTools(), i, m = 15;
o.jio = jIO.newJio({ function listDocumentsFromLocal() {
"type": "indexed", return jio_local.allDocs();
"indices": [ }
{"id": "A", "index": ["director"]},
{"id": "B", "index": ["title", "year"]}
],
"sub_storage": {
"type": "local",
"username": "icomplex",
"application_name": "acomplex"
}
});
o.localpath = "jio/localstorage/icomplex/acomplex";
o.getAttachmentCallback = function (err, response) { function listDocumentsFromLocalTest(answer) {
if (response) { if (answer && answer.data && Array.isArray(answer.data.rows)) {
try { answer.data.rows.sort(function (a, b) {
response = JSON.parse(response); return a.id.length < b.id.length ? -1 : (
} catch (e) { a.id.length > b.id.length ? 1 : (
response = "PARSE ERROR " + response; a.id < b.id ? -1 : a.id > b.id ? 1 : 0
} )
);
});
} }
o.f(err, response); deepEqual(answer, {
}; "data": {
"total_rows": 8,
// sample data "rows": [{
o.titles = [ "id": "A",
"Shawshank Redemption", "key": "A",
"Godfather", "value": {}
"Godfather 2", }, {
"Pulp Fiction", "id": "B",
"The Good, The Bad and The Ugly", "key": "B",
"12 Angry Men", "value": {}
"The Dark Knight", }, {
"Schindlers List", "id": "C",
"Lord of the Rings - Return of the King", "key": "C",
"Fight Club", "value": {}
"Star Wars Episode V", }, {
"Lord Of the Rings - Fellowship of the Ring", "id": "D",
"One flew over the Cuckoo's Nest", "key": "D",
"Inception", "Godfellas" "value": {}
]; }, {
o.years = [ "id": "b",
1994, "key": "b",
1972, "value": {}
1974, }, {
1994, "id": "ce",
1966, "key": "ce",
1957, "value": {}
2008, }, {
1993, "id": "dee",
2003, "key": "dee",
1999, "value": {}
1980, }, {
2001, "id": shared.created_document_id,
1975, "key": shared.created_document_id,
2010, "value": {}
1990 }]
]; },
o.director = [ "method": "allDocs",
"Frank Darabont", "result": "success",
"Francis Ford Coppola", "status": 200,
"Francis Ford Coppola", "statusText": "Ok"
"Quentin Tarantino", }, "List 8 documents from local (4 document + 4 databases)");
"Sergio Leone",
"Sidney Lumet",
"Christopher Nolan",
"Steven Spielberg",
"Peter Jackson",
"David Fincher",
"Irvin Kershner",
"Peter Jackson",
"Milos Forman",
"Christopher Nolan",
" Martin Scorsese"
];
o.fakeIndexA = {
"indexing": ["director"],
"free": [],
"location": {},
"database": []
};
o.fakeIndexB = {
"indexing": ["title", "year"],
"free": [],
"location": {},
"database": []
};
for (i = 0; i < m; i += 1) {
o.jio.put({
"_id": i.toString(),
"director": o.director[i],
"year": o.years[i],
"title": o.titles[i]
});
o.tmp = o.fakeIndexA.free.pop() || o.fakeIndexA.database.length;
o.fakeIndexA.database[o.tmp] = {
"_id": i.toString(),
"director": o.director[i]
};
o.fakeIndexA.location[i] = o.tmp;
o.tmp = o.fakeIndexB.free.pop() || o.fakeIndexB.database.length;
o.fakeIndexB.database[o.tmp] = {
"_id": i.toString(),
"year": o.years[i],
"title": o.titles[i]
};
o.fakeIndexB.location[i] = o.tmp;
o.clock.tick(1000);
} }
// o.clock.tick(1000);
// check index file
o.spy(o, "value", o.fakeIndexA, "Check index file");
o.jio.getAttachment({
"_id": "A",
"_attachment": "body"
}, o.getAttachmentCallback);
o.tick(o);
o.spy(o, "value", o.fakeIndexB, "Check index file"); // function removeCreatedDocuments() {
o.jio.getAttachment({ // return success(RSVP.all([
"_id": "B", // jio_index.remove({"_id": shared.created_document_id}),
"_attachment": "body" // jio_index.remove({"_id": "b"}),
}, o.getAttachmentCallback); // jio_index.remove({"_id": "ce"}),
o.tick(o); // jio_index.remove({"_id": "dee"})
// ]));
// }
// function removeCreatedDocumentsTest(answers) {
// deepEqual(answers[0], {
// "id": shared.created_document_id,
// "method": "remove",
// "result": "success",
// "status": 204,
// "statusText": "No Content"
// }, "Remove first document");
// deepEqual(answers[1], {
// "id": "b",
// "method": "remove",
// "result": "success",
// "status": 204,
// "statusText": "No Content"
// }, "Remove document 'b'");
// deepEqual(answers[2], {
// "id": "ce",
// "method": "remove",
// "result": "success",
// "status": 204,
// "statusText": "No Content"
// }, "Remove document 'ce'");
// deepEqual(answers[3], {
// "id": "dee",
// "method": "remove",
// "result": "success",
// "status": 204,
// "statusText": "No Content"
// }, "Remove document 'dee'");
// }
// XXX the 2 following functions should be replaced by the 2 commented
// previous ones (which don't work yet)
function removeCreatedDocuments() {
return sequence([function () {
return jio_index.remove({"_id": shared.created_document_id});
}, function () {
return jio_index.remove({"_id": "b"});
}, function () {
return jio_index.remove({"_id": "ce"});
}, function () {
return jio_index.remove({"_id": "dee"});
}]);
}
// response function removeCreatedDocumentsTest(last_answer) {
o.allDocsResponse = {}; deepEqual(last_answer, {
o.allDocsResponse.rows = []; "id": "dee",
o.allDocsResponse.total_rows = m; "method": "remove",
for (i = 0; i < m; i += 1) { "result": "success",
o.allDocsResponse.rows.push({ "status": 204,
"id": i.toString(), "statusText": "No Content"
"key": i.toString(), }, "Remove first document, 'b', 'ce' and 'dee' (testing 'dee' only)");
"value": {},
"doc": {
"_id": i.toString(),
"title": o.titles[i],
"year": o.years[i],
"director": o.director[i]
}
});
} }
o.response = JSON.parse(JSON.stringify(o.allDocsResponse)); function listEmptyIndexes() {
for (i = 0; i < o.response.rows.length; i += 1) { return RSVP.all([
delete o.response.rows[i].doc; success(jio_index.allDocs({"select_list": ["contributor"]})),
success(jio_index.allDocs({"select_list": ["title"]})),
success(jio_index.allDocs({"select_list": ["title", "year"]})),
success(jio_index.allDocs({"select_list": ["author"]})),
success(jio_index.allDocs())
]);
} }
// alldocs function listEmptyIndexesTest(answers) {
o.spy(o, "value", o.response, "AllDocs response generated from index"); deepEqual(answers[0], {
o.jio.allDocs(o.f); "data": {
o.tick(o, 1000); "total_rows": 7000,
"rows": []
},
"method": "allDocs",
"result": "success",
"status": 200,
"statusText": "Ok"
}, "List empty indexes 'contributor'");
deepEqual(answers[1], {
"data": {
"total_rows": 7000,
"rows": []
},
"method": "allDocs",
"result": "success",
"status": 204,
"statusText": "No Content"
}, "List empty indexes 'title'");
deepEqual(answers[2], {
"data": {
"total_rows": 7000,
"rows": []
},
"method": "allDocs",
"result": "success",
"status": 204,
"statusText": "No Content"
}, "List empty indexes 'title', 'year'");
deepEqual(answers[3], {
"data": {
"total_rows": 7000,
"rows": []
},
"method": "allDocs",
"result": "success",
"status": 204,
"statusText": "No Content"
}, "List empty indexes 'author'");
deepEqual(answers[4], {
"data": {
"total_rows": 7000,
"rows": []
},
"method": "allDocs",
"result": "success",
"status": 204,
"statusText": "No Content"
}, "List default empty indexes");
}
// complex queries // // XXX the 2 following functions should be replaced by the 2 commented
o.response = JSON.parse(JSON.stringify(o.allDocsResponse)); // // previous ones (which don't work yet)
i = 0; // function removeCreatedDocuments() {
while (i < o.response.rows.length) { // return sequence([function () {
if (o.response.rows[i].year < 1980) { // return jio_index.remove({"_id": shared.created_document_id});
o.response.rows.splice(i, 1); // }, function () {
// return jio_index.remove({"_id": "b"});
// }, function () {
// return jio_index.remove({"_id": "ce"});
// }, function () {
// return jio_index.remove({"_id": "dee"});
// }]);
// }
// function removeCreatedDocumentsTest(last_answer) {
// deepEqual(last_answer, {
// "id": "dee",
// "method": "remove",
// "result": "success",
// "status": 204,
// "statusText": "No Content"
// }, "Remove first document, 'b', 'ce' and 'dee' (testing 'dee' only)");
// }
function unexpectedError(error) {
if (error instanceof Error) {
deepEqual([
error.name + ": " + error.message,
error
], "UNEXPECTED ERROR", "Unexpected error");
} else { } else {
o.response.rows[i].value = { deepEqual(error, "UNEXPECTED ERROR", "Unexpected error");
"year": o.response.rows[i].doc.year,
"title": o.response.rows[i].doc.title
};
delete o.response.rows[i].doc;
i += 1;
} }
} }
o.response.rows.sort(function (a, b) {
return (a.value.year > b.value.year ? -1 :
a.value.year < b.value.year ? 1 : 0);
});
o.response.rows.length = 5;
o.response.total_rows = 5;
o.spy(o, "value", o.response,
"allDocs (complex queries year >= 1980, index used to do query)");
o.jio.allDocs({
// "query":'(year: >= "1980" AND year: < "2000")',
"query": '(year: >= "1980")',
"limit": [0, 5],
"sort_on": [['year', 'descending']],
"select_list": ['title', 'year']
}, o.f);
o.tick(o);
// complex queries
o.spy(o, "value", {"total_rows": 0, "rows": []},
"allDocs (complex queries year >= 1980, can't use index)");
o.jio.allDocs({
// "query":'(year: >= "1980" AND year: < "2000")',
"query": '(year: >= "1980")',
"limit": [0, 5],
"sort_on": [['year', 'descending']],
"select_list": ['director', 'year']
}, o.f);
o.tick(o);
// empty query returns all stop();
o.response = JSON.parse(JSON.stringify(o.allDocsResponse));
i = 0; // # Post new documents, list them and remove them
while (i < o.response.rows.length) { // post a 201
o.response.rows[i].value.title = postNewDocument().then(postNewDocumentTest).
o.response.rows[i].doc.title; // get 200
delete o.response.rows[i].doc; then(getCreatedDocument).then(getCreatedDocumentTest).
i += 1; // post b ce dee 201
} then(postSpecificDocuments).then(postSpecificDocumentsTest).
o.response.rows.sort(function (a, b) { // allD 200 1 documents from index contributor
return (a.value.title > b.value.title ? -1 : then(listDocumentsFromIndexContributor).
a.value.title < b.value.title ? 1 : 0); then(listDocumentsFromIndexContributorTest).
}); // allD 200 2 documents from index title
o.spy(o, "value", o.response, then(listDocumentsFromIndexTitle).
"allDocs (empty query in complex query)"); then(listDocumentsFromIndexTitleTest).
o.jio.allDocs({ // allD 200 2 documents from index title year
"sort_on": [['title', 'descending']], then(listDocumentsFromIndexTitleYear).
"select_list": ['title'] then(listDocumentsFromIndexTitleYearTest).
}, o.f); // allD 200 0 documents from index author
o.tick(o); then(listDocumentsFromIndexAuthor).
then(listDocumentsFromIndexAuthorTest).
// allD 200 0 documents from nothing (no select_list option)
then(listDocumentsFromNothing).
then(listDocumentsFromNothingTest).
// allD 200 8 documents from local
then(listDocumentsFromLocal).then(listDocumentsFromLocalTest).
// remove a b ce dee 204
then(removeCreatedDocuments).then(removeCreatedDocumentsTest).
// allD 200 empty indexes
then(listEmptyIndexes).then(listEmptyIndexesTest).
// // # Create and update documents, and some attachment and remove them
// // put 201
// then(putNewDocument).then(putNewDocumentTest).
// // get 200
// then(getCreatedDocument2).then(getCreatedDocument2Test).
// // post 409
// then(postSameDocument).then(postSameDocumentTest).
// // putA a 204
// then(createAttachment).then(createAttachmentTest).
// // putA a 204
// then(updateAttachment).then(updateAttachmentTest).
// // putA b 204
// then(createAnotherAttachment).then(createAnotherAttachmentTest).
// // put 204
// then(updateLastDocument).then(updateLastDocumentTest).
// // getA a 200
// then(getFirstAttachment).then(getFirstAttachmentTest).
// // getA b 200
// then(getSecondAttachment).then(getSecondAttachmentTest).
// // get 200
// then(getLastDocument).then(getLastDocumentTest).
// // removeA b 204
// then(removeSecondAttachment).then(removeSecondAttachmentTest).
// // getA b 404
// then(getInexistentSecondAttachment).
// then(getInexistentSecondAttachmentTest).
// // get 200
// then(getOneAttachmentDocument).then(getOneAttachmentDocumentTest).
// // removeA b 404
//then(removeSecondAttachmentAgain).then(removeSecondAttachmentAgainTest).
// // remove 204
// then(removeDocument).then(removeDocumentTest).
// // getA a 404
//then(getInexistentFirstAttachment)
//.then(getInexistentFirstAttachmentTest).
// // get 404
// then(getInexistentDocument).then(getInexistentDocumentTest).
// // remove 404
// then(removeInexistentDocument).then(removeInexistentDocumentTest).
// // check 204
// //then(checkDocument).done(checkDocumentTest).
// //then(checkStorage).done(checkStorageTest).
fail(unexpectedError).
always(start);
util.closeAndcleanUpJio(o.jio);
}); });
})); }));
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