diff --git a/src/jio.storage/indexstorage.js b/src/jio.storage/indexstorage.js index 07a51feb9a0e4130436c2c496d13ac746e12a40b..cb77d0e17a555a786a4ce34f1fbe3fce7ec14492 100644 --- a/src/jio.storage/indexstorage.js +++ b/src/jio.storage/indexstorage.js @@ -17,7 +17,7 @@ */ /*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. @@ -118,131 +118,23 @@ if (typeof define === 'function' && define.amd) { return define(dependencies, module); } - module(jIO, complex_queries); -}(['jio', 'complex_queries'], function (jIO, complex_queries) { - "use strict"; - - var error_dict = { - "Corrupted Index": { - "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; - }); + if (typeof exports === 'object') { + return module( + exports, + require('jio'), + require('rsvp'), + require('complex_queries') + ); } + 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 @@ -314,7 +206,7 @@ */ that.put = function (meta) { 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"); } for (k in meta) { @@ -359,7 +251,8 @@ throw new TypeError("Corrupted Metadata"); } 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._free.push(that._location[meta._id]); @@ -388,7 +281,8 @@ for (id in that._location) { if (that._location.hasOwnProperty(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) { throw new TypeError("Corrupted Index"); } @@ -421,8 +315,11 @@ that.checkDocument = function (doc) { var i, key, db_doc; - if (typeof that._location[doc._id] !== "number" || - (db_doc = that._database(that._location[doc._id])._id) !== doc._id) { + if (typeof that._location[doc._id] !== "number") { + throw new TypeError("Different Index"); + } + db_doc = that._database(that._location[doc._id])._id; + if (db_doc !== doc._id) { throw new TypeError("Different Index"); } for (i = 0; i < that._indexing.length; i += 1) { @@ -442,12 +339,13 @@ var i = 0, meta; that._free = []; that._location = {}; - if (type(that._database) !== "Array") { + if (!Array.isArray(that._database)) { that._database = []; } while (i < that._database.length) { meta = that._database[i]; - if (type(meta) === "Object" && + if (typeof meta === 'object' && + Object.getPrototypeOf(meta || []) === Object.prototype && typeof meta._id === "string" && meta._id !== "" && !that._location[meta._id]) { that._location[meta._id] = i; @@ -461,10 +359,10 @@ /** * Returns the serialized version of this object (not cloned) * - * @method serialized + * @method toJSON * @return {Object} The serialized version */ - that.serialized = function () { + that.toJSON = function () { return { "indexing": that._indexing, "free": that._free, @@ -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) { - var that, priv = {}; + function similarityPercentage(list_a, list_b) { + 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 () { - return { - "indices": priv.indices, - "sub_storage": priv.sub_storage - }; - }; + IndexStorage.prototype.getIndexDatabases = function (command) { + var i, promises = []; + for (i = 0; i < this._indices.length; i += 1) { + promises[promises.length] = this.getIndexDatabase(command, i); + } + return RSVP.all(promises); + }; - /** - * Return the similarity percentage (1 >= p >= 0) between two index lists. - * - * @method similarityPercentage - * @param {Array} list_a An index list - * @param {Array} list_b Another index list - * @return {Number} The similarity percentage - */ - priv.similarityPercentage = function (list_a, list_b) { - 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; - } - } + IndexStorage.prototype.storeIndexDatabase = function (command, database, + index) { + var that = this; + index = this._indices[index]; + function putAttachment() { + return command.storage( + index.sub_storage || that._sub_storage + ).putAttachment({ + "_id": index.id, + "_attachment": index.attachment || "body", + "_data": JSON.stringify(database), + "_content_type": "application/json" + }); + } + 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 ? - list_a.length : list_b.length); - }; + throw err; + } + return putAttachment(). + then(null, createDatabaseAndPutAttachmentIfPossible); + }; - /** - * 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 - */ - 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; - } + IndexStorage.prototype.storeIndexDatabases = function (command, databases) { + var i, promises = []; + for (i = 0; i < this._indices.length; i += 1) { + if (databases[i] !== undefined) { + promises[promises.length] = + this.storeIndexDatabase(command, databases[i], i); } - 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. - * - * @method getIndexDatabaseList - * @param {Object} option The command option - * @param {Function} callback The result callback(database_list) - */ - priv.getIndexDatabaseList = function (option, callback) { - var i, count = 0, callbacks = {}, response_list = []; - callbacks.error = function (index) { - return function (err) { - if (err.status === 404) { - response_list[index] = new JSONIndex({ - "_id": priv.indices[index].id, - "_attachment": priv.indices[index].attachment || "body", - "indexing": priv.indices[index].index - }); - count += 1; - 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) - ); + /** + * Generic method for 'post', 'put', 'get' and 'remove'. It delegates the + * command to the sub storage and update the databases. + * + * @method genericCommand + * @param {String} method The method to use + * @param {Object} command The JIO command + * @param {Object} metadata The metadata to post + * @param {Object} option The command option + */ + IndexStorage.prototype.genericCommand = function (method, command, + metadata, option) { + var that = this, generic_response; + function updateAndStoreIndexDatabases(responses) { + var i, database_list = responses[0]; + generic_response = responses[1]; + if (method === 'get') { + jIO.util.dictUpdate(metadata, generic_response.data); } - }; - - /** - * Saves all the databases to the remote(s). - * - * @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}); + metadata._id = generic_response.id; + if (method === 'remove') { + for (i = 0; i < database_list.length; i += 1) { + database_list[i].remove(metadata); } - } - function onAttachmentError(err) { - err.message = "Unable to store index database."; - 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); + } else { + for (i = 0; i < database_list.length; i += 1) { + database_list[i].put(metadata); } } - }; + return that.storeIndexDatabases(command, database_list); + } - /** - * A generic request method which delegates the request to the sub storage. - * On response, it will index the document from the request and update all - * the databases. - * - * @method genericRequest - * @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 allProgress(progress) { + if (progress.index === 1) { + progress.value.percentage *= 0.7; // 0 to 70% + command.notify(progress.value); + } + throw null; // stop propagation + } - /** - * Post the document metadata and update the index - * @method post - * @param {object} command The JIO command - */ - that.post = function (command) { - priv.genericRequest(command, 'post'); - }; + function success() { + command.success(generic_response); + } - /** - * Update the document metadata and update the index - * @method put - * @param {object} command The JIO command - */ - that.put = function (command) { - priv.genericRequest(command, 'put'); - }; + function storeProgress(progress) { + progress.percentage = (0.3 * progress.percentage) + 70; // 70 to 100% + command.notify(progress); + } - /** - * Add an attachment to a document (no index modification) - * @method putAttachment - * @param {object} command The JIO command - */ - that.putAttachment = function (command) { - priv.genericRequest(command, 'putAttachment'); - }; + RSVP.all([ + this.getIndexDatabases(command), + command.storage(this._sub_storage)[method](metadata, option) + ]).then(updateAndStoreIndexDatabases, null, allProgress). + then(success, command.error, storeProgress); + }; - /** - * Get the document metadata - * @method get - * @param {object} command The JIO command - */ - that.get = function (command) { - priv.genericRequest(command, 'get'); - }; + /** + * Post the document metadata and update the index + * + * @method post + * @param {Object} command The JIO command + * @param {Object} metadata The metadata to post + * @param {Object} option The command option + */ + IndexStorage.prototype.post = function (command, metadata, option) { + this.genericCommand('post', command, metadata, option); + }; - /** - * Get the attachment. - * @method getAttachment - * @param {object} command The JIO command - */ - that.getAttachment = function (command) { - priv.genericRequest(command, 'getAttachment'); - }; + /** + * Update the document metadata and update the index + * + * @method put + * @param {Object} command The JIO command + * @param {Object} metadata The metadata to put + * @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!. - * @method remove - * @param {object} command The JIO command - */ - that.remove = function (command) { - priv.genericRequest(command, 'remove'); - }; + /** + * Add an attachment to a document (no index modification) + * + * @method putAttachment + * @param {Object} command The JIO command + * @param {Object} param The command parameters + * @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 - * @method removeAttachment - * @param {object} command The JIO command - */ - that.removeAttachment = function (command) { - priv.genericRequest(command, 'removeAttachment'); - }; + /** + * Get the document metadata + * + * @method get + * @param {Object} command The JIO command + * @param {Object} param The command parameters + * @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 - * Options: - * - {boolean} include_docs Also retrieve the actual document content. - * @method allDocs - * @param {object} command The JIO command - */ - that.allDocs = function (command) { - var option = command.cloneOption(), - index = priv.selectIndex(option.select_list || []); - // Include docs option is ignored, if you want to get all the document, - // don't use index storage! + /** + * Get the attachment. + * + * @method getAttachment + * @param {Object} command The JIO command + * @param {Object} param The command parameters + * @param {Object} option The command option + */ + IndexStorage.prototype.getAttachment = function (command, param, option) { + command.storage(this._sub_storage).getAttachment(param, option). + then(command.success, command.error, command.notify); + }; - option.select_list = option.select_list || []; - option.select_list.push("_id"); - priv.getIndexDatabase(option, index, 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; - delete db[i]._id; - db[i] = { - "id": id, - "key": id, - "value": db[i], - }; - } - that.success({"total_rows": db.length, "rows": db}); - }); - }; + /** + * Remove document - removing documents updates index!. + * + * @method remove + * @param {Object} command The JIO command + * @param {Object} param The command parameters + * @param {Object} option The command option + */ + IndexStorage.prototype.remove = function (command, param, option) { + this.genericCommand('remove', command, param, option); + }; - 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(); - that.addJob( - 'allDocs', - priv.sub_storage, - {}, - {'include_docs': true}, - function (response) { - var db_list = [], db = new JSONIndex({ - "_id": command.getDocId(), - "_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); - } - ); - }; + /** + * Gets a document list from the substorage + * Options: + * - {boolean} include_docs Also retrieve the actual document content. + * @method allDocs + * @param {object} command The JIO command + */ + IndexStorage.prototype.allDocs = function (command, param, option) { // XXX + /*jslint unparam: true */ + var index = this.selectIndex(option.select_list || []), delete_id; - priv.repairDocument = function (command, just_check) { - var i, option = command.cloneOption(); - 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); - } - ); - }; + // Include docs option is ignored, if you want to get all the document, + // don't use index storage! - that.repair = function (command, just_check) { - var database_index = -1, i; - for (i = 0; i < priv.indices.length; i += 1) { - if (priv.indices[i].id === command.getDocId()) { - database_index = i; - break; + option.select_list = ( + Array.isArray(option.select_list) ? option.select_list : [] + ); + if (option.select_list.indexOf("_id") === -1) { + option.select_list.push("_id"); + 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( - "repair", - priv.sub_storage, - command.cloneDoc(), - command.cloneOption(), - function (response) { - 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); - } - ); - }; + command.success(200, {"data": {"total_rows": db.length, "rows": db}}); + }, function (err) { + if (err.status === 404) { + return command.success(200, {"data": {"total_rows": 0, "rows": []}}); + } + command.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); })); diff --git a/test/jio.storage/indexstorage.tests.js b/test/jio.storage/indexstorage.tests.js index 9f317ca0f1a5017b41ca142e7a8c4a961371e553..f2e88b309b50df34eef93fe9670f17240b1c727e 100644 --- a/test/jio.storage/indexstorage.tests.js +++ b/test/jio.storage/indexstorage.tests.js @@ -1,5 +1,6 @@ /*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); (function (dependencies, module) { @@ -7,1108 +8,621 @@ if (typeof define === 'function' && define.amd) { return define(dependencies, module); } - module(jIO, jio_tests); -}(['jio', 'jio_tests', 'localstorage', 'indexstorage'], function (jIO, util) { + module(test_util, RSVP, jIO, local_storage); +}([ + 'test_util', + 'rsvp', + 'jio', + 'localstorage', + 'indexstorage' +], function (util, RSVP, jIO, local_storage) { "use strict"; - function generateTools() { - return { - clock: sinon.useFakeTimers(), - spy: util.ospy, - tick: util.otick - }; - } - - 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" - } + function success(promise) { + return new RSVP.Promise(function (resolve, reject, notify) { + /*jslint unparam: true*/ + promise.then(resolve, resolve, notify); + }, function () { + promise.cancel(); }); + } - o.getAttachmentCallback = function (err, response) { - if (response) { - try { - response = JSON.parse(response); - } catch (e) { - response = "PARSE ERROR " + response; + /** + * sequence(thens): Promise + * + * Executes a sequence of *then* callbacks. It acts like + * `smth().then(callback).then(callback)...`. The first callback is called + * 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); - }; - - // post without id - o.spy(o, "jobstatus", "done", "Post without id"); - o.jio.post({}, function (err, response) { - o.id = (response || {}).id; - o.f(err, response); - }); - o.tick(o); - - // post non empty document - o.doc = {"_id": "some_id", "title": "My Title", - "year": 2000, "hey": "def"}; - o.spy(o, "value", {"ok": true, "id": "some_id"}, "Post document"); - o.jio.post(o.doc, o.f); - o.tick(o); - - // check document - o.fakeIndexA = { - "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" + promises[i].then(resolve, reject, notify); + }, function () { + var i; + for (i = 0; i < promises.length; i += 1) { + promises[i].cancel(); } }); + } - o.getAttachmentCallback = function (err, response) { - 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); - }); + module("IndexStorage"); - test("Check & Repair", function () { - var o = generateTools(), i; + test("Scenario", function () { - o.jio = jIO.newJio({ + var LOCAL_STORAGE_SPEC = local_storage.createDescription( + 'indexstorage tests', + 'scenario', + 'memory' + ), INDEX_STORAGE_SPEC = { "type": "indexed", "indices": [ - {"id": "A", "index": ["director"]}, - {"id": "B", "index": ["year"]} + {"id": "A", "index": ["contributor"]}, + {"id": "B", "index": ["author"]}, + {"id": "C", "index": ["title"]}, + {"id": "D", "index": ["title", "year"]} ], - "sub_storage": { - "type": "local", - "username": "indexstoragerepair" - } - }); + "sub_storage": LOCAL_STORAGE_SPEC + }, option = {"workspace": {}}, shared = {}, jio_index, jio_local; - o.getAttachmentCallback = function (err, response) { - if (response) { - 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}; + jio_index = jIO.createJIO(INDEX_STORAGE_SPEC, option); + jio_local = jIO.createJIO(LOCAL_STORAGE_SPEC, option); - o.tmp = o.fakeIndexB.free.pop() || o.fakeIndexB.database.length; - o.fakeIndexB.database[o.tmp] = {"_id": "id" + i, "year": i}; + function postNewDocument() { + 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 () { - - // 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({ - "type": "indexed", - "indices": [ - {"id": "A", "index": ["author"]}, - {"id": "B", "index": ["year"]} - ], - "sub_storage": { - "type": "local", - "username": "iputatt", - "application_name": "iputatt" - } - }); - - // putAttachment without doc id - // error 20 -> document id required - o.spy(o, "status", 20, "PutAttachment without doc id"); - o.jio.putAttachment({}, o.f); - o.tick(o); - - // putAttachment without attachment id - // error 22 -> attachment id required - o.spy(o, "status", 22, "PutAttachment without attachment id"); - o.jio.putAttachment({"_id": "putattmt1"}, o.f); - o.tick(o); - - // putAttachment without document - // error 404 -> not found - o.spy(o, "status", 404, "PutAttachment without document"); - o.jio.putAttachment({"_id": "putattmt1", "_attachment": "putattmt2"}, o.f); - o.tick(o); + function postNewDocumentTest(answer) { + var uuid = answer.id; + answer.id = "<uuid>"; + deepEqual(answer, { + "id": "<uuid>", + "method": "post", + "result": "success", + "status": 201, + "statusText": "Created" + }, "Post a new document"); + ok(util.isUuid(uuid), "New document id should look like " + + "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx : " + uuid); + shared.created_document_id = uuid; + } - // putAttachment with document - o.doc = {"_id": "putattmt1", "title": "myPutAttmt1"}; - o.spy(o, "value", {"ok": true, "id": "putattmt1"}, - "Put underlying document"); - o.jio.put(o.doc, o.f); - o.tick(o); + function getCreatedDocument() { + return jio_index.get({"_id": shared.created_document_id}); + } - o.spy(o, "value", { - "ok": true, - "id": "putattmt1", - "attachment": "putattmt2" - }, "PutAttachment with document, without data"); - o.jio.putAttachment({"_id": "putattmt1", "_attachment": "putattmt2"}, o.f); - o.tick(o); + function getCreatedDocumentTest(answer) { + deepEqual(answer, { + "data": { + "_id": shared.created_document_id, + "title": "Unique ID" + }, + "id": shared.created_document_id, + "method": "get", + "result": "success", + "status": 200, + "statusText": "Ok" + }, "Get new document"); + } - // check document - deepEqual(util.jsonlocalstorage.getItem( - "jio/localstorage/iputatt/iputatt/putattmt1" - ), { - "_id": "putattmt1", - "title": "myPutAttmt1", - "_attachments": { - "putattmt2": { - "length": 0, - // md5("") - "digest": "md5-d41d8cd98f00b204e9800998ecf8427e" - } - } - }, "Check document"); + // function postSpecificDocuments() { + // return success(RSVP.all([ + // jio_index.post({"_id": "b", "title": "Bee", "year": 2013}), + // jio_index.post({"_id": "ce", "contributor": "DCee"}), + // jio_index.post({"_id": "dee", "format": "text/plain"}) + // ])); + // } + + // function postSpecificDocumentsTest(answers) { + // deepEqual(answers[0], { + // "id": "b", + // "method": "post", + // "result": "success", + // "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 - deepEqual(util.jsonlocalstorage.getItem( - "jio/localstorage/iputatt/iputatt/putattmt1/putattmt2" - ), "", "Check attachment"); + function postSpecificDocumentsTest(last_answer) { + deepEqual(last_answer, { + "id": "dee", + "method": "post", + "result": "success", + "status": 201, + "statusText": "Created" + }, "Post documents: 'b', 'ce', 'dee' (testing 'dee' response only)"); + } - // update attachment - o.spy(o, "value", - {"ok": true, "id": "putattmt1", "attachment": "putattmt2"}, - "Update Attachment, with data"); - o.jio.putAttachment({ - "_id": "putattmt1", - "_attachment": "putattmt2", - "_data": "abc" - }, o.f); - o.tick(o); + function listDocumentsFromIndexContributor() { + return jio_index.allDocs({"select_list": ["contributor"]}); + } - // check document - deepEqual(util.jsonlocalstorage.getItem( - "jio/localstorage/iputatt/iputatt/putattmt1" - ), { - "_id": "putattmt1", - "title": "myPutAttmt1", - "_attachments": { - "putattmt2": { - "length": 3, - // md5("abc") - "digest": "md5-900150983cd24fb0d6963f7d28e17f72" - } + function listDocumentsFromIndexContributorTest(answer) { + if (answer && answer.data && Array.isArray(answer.data.rows)) { + answer.data.rows.sort(function (a, b) { + return a.id.length < b.id.length ? -1 : ( + a.id.length > b.id.length ? 1 : 0 + ); + }); } - }, "Check document"); - - // check attachment - deepEqual(util.jsonlocalstorage.getItem( - "jio/localstorage/iputatt/iputatt/putattmt1/putattmt2" - ), "abc", "Check attachment"); - - util.closeAndcleanUpJio(o.jio); - }); - - test("Get", function () { + deepEqual(answer, { + "data": { + "total_rows": 1, + "rows": [{ + "id": "ce", + "value": {"contributor": "DCee"} + }] + }, + "method": "allDocs", + "result": "success", + "status": 200, + "statusText": "Ok" + }, "List 1 document from 'contributor'"); + } - // 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(); + function listDocumentsFromIndexTitleYear() { + return jio_index.allDocs({"select_list": ["year", "title"]}); + } - o.jio = jIO.newJio({ - "type": "indexed", - "indices": [ - {"id": "A", "index": ["author"]}, - {"id": "B", "index": ["year"]} - ], - "sub_storage": { - "type": "local", - "username": "iget", - "application_name": "iget" + function listDocumentsFromIndexTitleYearTest(answer) { + if (answer && answer.data && Array.isArray(answer.data.rows)) { + answer.data.rows.sort(function (a, b) { + return a.id.length < b.id.length ? -1 : ( + a.id.length > b.id.length ? 1 : 0 + ); + }); } - }); - - // get inexistent document - o.spy(o, "status", 404, "Get inexistent document"); - o.jio.get({"_id": "get1"}, o.f); - o.tick(o); - - // get inexistent attachment - o.spy(o, "status", 404, "Get inexistent attachment"); - o.jio.getAttachment({"_id": "get1", "_attachment": "get2"}, o.f); - o.tick(o); - - // adding a document - o.doc_get1 = { - "_id": "get1", - "title": "myGet1" - }; - 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); + deepEqual(answer, { + "data": { + "total_rows": 2, + "rows": [{ + "id": "b", + "value": {"title": "Bee", "year": 2013} + }, { + "id": shared.created_document_id, + "value": {"title": "Unique ID"} + }] + }, + "method": "allDocs", + "result": "success", + "status": 200, + "statusText": "Ok" + }, "List 2 documents from 'year' and 'title'"); + } - // get inexistent attachment (document exists) - o.spy(o, "status", 404, "Get inexistent attachment (document exists)"); - o.jio.getAttachment({"_id": "get1", "_attachment": "get2"}, o.f); - o.tick(o); + function listDocumentsFromIndexTitle() { + return jio_index.allDocs({"select_list": ["title"]}); + } - // adding an attachment - o.doc_get1._attachments = { - "get2": { - "length": 2, - // md5("de") - "digest": "md5-5f02f0889301fd7be1ac972c11bf3e7d" + function listDocumentsFromIndexTitleTest(answer) { + if (answer && answer.data && Array.isArray(answer.data.rows)) { + answer.data.rows.sort(function (a, b) { + return a.id.length < b.id.length ? -1 : ( + a.id.length > b.id.length ? 1 : 0 + ); + }); } - }; - util.jsonlocalstorage.setItem( - "jio/localstorage/iget/iget/get1", - o.doc_get1 - ); - util.jsonlocalstorage.setItem("jio/localstorage/iget/iget/get1/get2", "de"); - - // get attachment - o.spy(o, "value", "de", "Get attachment"); - o.jio.getAttachment({"_id": "get1", "_attachment": "get2"}, o.f); - o.tick(o); - - util.closeAndcleanUpJio(o.jio); - }); - - test("Remove", function () { - - // not sure these need to be run, because the index does not change - // and only small modifications have been made to handle putAttachment - // tests are from localStorage putAttachment - var o = generateTools(); + deepEqual(answer, { + "data": { + "total_rows": 2, + "rows": [{ + "id": "b", + "value": {"title": "Bee"} + }, { + "id": shared.created_document_id, + "value": {"title": "Unique ID"} + }] + }, + "method": "allDocs", + "result": "success", + "status": 200, + "statusText": "Ok" + }, "List 2 documents from 'title'"); + } - o.jio = jIO.newJio({ - "type": "indexed", - "indices": [ - {"id": "A", "index": ["author"]}, - {"id": "B", "index": ["year"]} - ], - "sub_storage": { - "type": "local", - "username": "irem", - "application_name": "irem" - } - }); + function listDocumentsFromIndexAuthor() { + return jio_index.allDocs({"select_list": ["author"]}); + } - o.getAttachmentCallback = function (err, response) { - if (response) { - try { - response = JSON.parse(response); - } catch (e) { - response = "PARSE ERROR " + response; - } + function listDocumentsFromIndexAuthorTest(answer) { + if (answer && answer.data && Array.isArray(answer.data.rows)) { + answer.data.rows.sort(function (a, b) { + return a.id.length < b.id.length ? -1 : ( + a.id.length > b.id.length ? 1 : 0 + ); + }); } - o.f(err, response); - }; - - // remove inexistent document - o.spy(o, "status", 404, "Remove inexistent document"); - o.jio.remove({"_id": "remove1"}, o.f); - o.tick(o); - - // remove inexistent document/attachment - o.spy(o, "status", 404, "Remove inexistent attachment"); - o.jio.removeAttachment({"_id": "remove1", "_attachment": "remove2"}, o.f); - o.tick(o); - - // adding a document - o.jio.put({"_id": "remove1", "title": "myRemove1", - "author": "Mr. President", "year": "2525" - }); - o.tick(o); - - // adding a 2nd document with same keywords - o.jio.put({"_id": "removeAlso", "title": "myRemove2", - "author": "Martin Mustermann", "year": "2525" - }); - o.tick(o); - - // remove document - o.spy(o, "value", {"ok": true, "id": "remove1"}, "Remove document"); - o.jio.remove({"_id": "remove1"}, o.f); - o.tick(o); - - // check index - o.fakeIndexA = { - "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(); + deepEqual(answer, { + "data": { + "total_rows": 0, + "rows": [] + }, + "method": "allDocs", + "result": "success", + "status": 200, + "statusText": "Ok" + }, "List 0 document from 'author'"); + } - o.jio = jIO.newJio({ - "type": "indexed", - "indices": [ - {"id": "A", "index": ["author"]}, - {"id": "B", "index": ["year"]} - ], - "sub_storage": { - "type": "local", - "username": "iall", - "application_name": "iall" - } - }); + function listDocumentsFromNothing() { + return jio_index.allDocs(); + } - o.getAttachmentCallback = function (err, response) { - if (response) { - try { - response = JSON.parse(response); - } catch (e) { - response = "PARSE ERROR " + response; - } + function listDocumentsFromNothingTest(answer) { + if (answer && answer.data && Array.isArray(answer.data.rows)) { + answer.data.rows.sort(function (a, b) { + return a.id.length < b.id.length ? -1 : ( + a.id.length > b.id.length ? 1 : 0 + ); + }); } - o.f(err, response); - }; - - // adding documents - o.all1 = { "_id": "dragon.doc", - "title": "some title", "author": "Dr. No", "year": "1968" - }; - o.spy(o, "value", {"ok": true, "id": "dragon.doc"}, "Put 1"); - o.jio.put(o.all1, o.f); - o.tick(o); - o.all2 = { - "_id": "timemachine", - "title": "hello world", - "author": "Dr. Who", - "year": "1968" - }; - o.spy(o, "value", {"ok": true, "id": "timemachine"}, "Put 2"); - o.jio.put(o.all2, o.f); - o.tick(o); - o.all3 = { - "_id": "rocket.ppt", - "title": "sunshine.", - "author": "Dr. Snuggles", - "year": "1985" - }; - o.spy(o, "value", {"ok": true, "id": "rocket.ppt"}, "Put 3"); - o.jio.put(o.all3, o.f); - o.tick(o); - o.all4 = { - "_id": "stick.jpg", - "title": "clouds", - "author": "Dr. House", - "year": "2005" - }; - o.spy(o, "value", {"ok": true, "id": "stick.jpg"}, "Put 4"); - o.jio.put(o.all4, o.f); - o.tick(o); - - // check index - o.fakeIndexA = { - "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; + deepEqual(answer, { + "data": { + "total_rows": 1, + "rows": [{ + "id": "ce", + "value": {} + }] + }, + "method": "allDocs", + "result": "success", + "status": 200, + "statusText": "Ok" + }, "List 1 document from first index (`allDocs()`)"); + } - o.jio = jIO.newJio({ - "type": "indexed", - "indices": [ - {"id": "A", "index": ["director"]}, - {"id": "B", "index": ["title", "year"]} - ], - "sub_storage": { - "type": "local", - "username": "icomplex", - "application_name": "acomplex" - } - }); - o.localpath = "jio/localstorage/icomplex/acomplex"; + function listDocumentsFromLocal() { + return jio_local.allDocs(); + } - o.getAttachmentCallback = function (err, response) { - if (response) { - try { - response = JSON.parse(response); - } catch (e) { - response = "PARSE ERROR " + response; - } + function listDocumentsFromLocalTest(answer) { + if (answer && answer.data && Array.isArray(answer.data.rows)) { + answer.data.rows.sort(function (a, b) { + return a.id.length < b.id.length ? -1 : ( + a.id.length > b.id.length ? 1 : ( + a.id < b.id ? -1 : a.id > b.id ? 1 : 0 + ) + ); + }); } - o.f(err, response); - }; - - // sample data - o.titles = [ - "Shawshank Redemption", - "Godfather", - "Godfather 2", - "Pulp Fiction", - "The Good, The Bad and The Ugly", - "12 Angry Men", - "The Dark Knight", - "Schindlers List", - "Lord of the Rings - Return of the King", - "Fight Club", - "Star Wars Episode V", - "Lord Of the Rings - Fellowship of the Ring", - "One flew over the Cuckoo's Nest", - "Inception", "Godfellas" - ]; - o.years = [ - 1994, - 1972, - 1974, - 1994, - 1966, - 1957, - 2008, - 1993, - 2003, - 1999, - 1980, - 2001, - 1975, - 2010, - 1990 - ]; - o.director = [ - "Frank Darabont", - "Francis Ford Coppola", - "Francis Ford Coppola", - "Quentin Tarantino", - "Sergio Leone", - "Sidney Lumet", - "Christopher Nolan", - "Steven Spielberg", - "Peter Jackson", - "David Fincher", - "Irvin Kershner", - "Peter Jackson", - "Milos Forman", - "Christopher Nolan", - " Martin Scorsese" - ]; - - o.fakeIndexA = { - "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); + deepEqual(answer, { + "data": { + "total_rows": 8, + "rows": [{ + "id": "A", + "key": "A", + "value": {} + }, { + "id": "B", + "key": "B", + "value": {} + }, { + "id": "C", + "key": "C", + "value": {} + }, { + "id": "D", + "key": "D", + "value": {} + }, { + "id": "b", + "key": "b", + "value": {} + }, { + "id": "ce", + "key": "ce", + "value": {} + }, { + "id": "dee", + "key": "dee", + "value": {} + }, { + "id": shared.created_document_id, + "key": shared.created_document_id, + "value": {} + }] + }, + "method": "allDocs", + "result": "success", + "status": 200, + "statusText": "Ok" + }, "List 8 documents from local (4 document + 4 databases)"); } - // 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"); - o.jio.getAttachment({ - "_id": "B", - "_attachment": "body" - }, o.getAttachmentCallback); - o.tick(o); + // function removeCreatedDocuments() { + // return success(RSVP.all([ + // jio_index.remove({"_id": shared.created_document_id}), + // jio_index.remove({"_id": "b"}), + // jio_index.remove({"_id": "ce"}), + // 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 - o.allDocsResponse = {}; - o.allDocsResponse.rows = []; - o.allDocsResponse.total_rows = m; - for (i = 0; i < m; i += 1) { - o.allDocsResponse.rows.push({ - "id": i.toString(), - "key": i.toString(), - "value": {}, - "doc": { - "_id": i.toString(), - "title": o.titles[i], - "year": o.years[i], - "director": o.director[i] - } - }); + 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)"); } - o.response = JSON.parse(JSON.stringify(o.allDocsResponse)); - for (i = 0; i < o.response.rows.length; i += 1) { - delete o.response.rows[i].doc; + function listEmptyIndexes() { + return RSVP.all([ + 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 - o.spy(o, "value", o.response, "AllDocs response generated from index"); - o.jio.allDocs(o.f); - o.tick(o, 1000); + function listEmptyIndexesTest(answers) { + deepEqual(answers[0], { + "data": { + "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 - o.response = JSON.parse(JSON.stringify(o.allDocsResponse)); - i = 0; - while (i < o.response.rows.length) { - if (o.response.rows[i].year < 1980) { - o.response.rows.splice(i, 1); + // // 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"}); + // }]); + // } + + // 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 { - o.response.rows[i].value = { - "year": o.response.rows[i].doc.year, - "title": o.response.rows[i].doc.title - }; - delete o.response.rows[i].doc; - i += 1; + deepEqual(error, "UNEXPECTED ERROR", "Unexpected error"); } } - o.response.rows.sort(function (a, b) { - return (a.value.year > b.value.year ? -1 : - a.value.year < b.value.year ? 1 : 0); - }); - o.response.rows.length = 5; - o.response.total_rows = 5; - o.spy(o, "value", o.response, - "allDocs (complex queries year >= 1980, index used to do query)"); - o.jio.allDocs({ - // "query":'(year: >= "1980" AND year: < "2000")', - "query": '(year: >= "1980")', - "limit": [0, 5], - "sort_on": [['year', 'descending']], - "select_list": ['title', 'year'] - }, o.f); - o.tick(o); - - // complex queries - o.spy(o, "value", {"total_rows": 0, "rows": []}, - "allDocs (complex queries year >= 1980, can't use index)"); - o.jio.allDocs({ - // "query":'(year: >= "1980" AND year: < "2000")', - "query": '(year: >= "1980")', - "limit": [0, 5], - "sort_on": [['year', 'descending']], - "select_list": ['director', 'year'] - }, o.f); - o.tick(o); - // empty query returns all - o.response = JSON.parse(JSON.stringify(o.allDocsResponse)); - i = 0; - while (i < o.response.rows.length) { - o.response.rows[i].value.title = - o.response.rows[i].doc.title; - delete o.response.rows[i].doc; - i += 1; - } - o.response.rows.sort(function (a, b) { - return (a.value.title > b.value.title ? -1 : - a.value.title < b.value.title ? 1 : 0); - }); - o.spy(o, "value", o.response, - "allDocs (empty query in complex query)"); - o.jio.allDocs({ - "sort_on": [['title', 'descending']], - "select_list": ['title'] - }, o.f); - o.tick(o); + stop(); + + // # Post new documents, list them and remove them + // post a 201 + postNewDocument().then(postNewDocumentTest). + // get 200 + then(getCreatedDocument).then(getCreatedDocumentTest). + // post b ce dee 201 + then(postSpecificDocuments).then(postSpecificDocumentsTest). + // allD 200 1 documents from index contributor + then(listDocumentsFromIndexContributor). + then(listDocumentsFromIndexContributorTest). + // allD 200 2 documents from index title + then(listDocumentsFromIndexTitle). + then(listDocumentsFromIndexTitleTest). + // allD 200 2 documents from index title year + then(listDocumentsFromIndexTitleYear). + then(listDocumentsFromIndexTitleYearTest). + // allD 200 0 documents from index author + 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); }); + }));