From 23189f9c405e374e1f00e1df06cbf95eaf9128f8 Mon Sep 17 00:00:00 2001
From: Tristan Cavelier <tristan.cavelier@tiolive.com>
Date: Tue, 6 Aug 2013 14:53:53 +0200
Subject: [PATCH] jiotests.js jio tests + local storage tests detached

---
 test/jio.storage/localstorage.tests.js | 561 +++++++++++++++++++
 test/jio/tests.js                      | 746 +++++++++++++++++++++++++
 test/jiotests.js                       | 683 ----------------------
 3 files changed, 1307 insertions(+), 683 deletions(-)
 create mode 100644 test/jio.storage/localstorage.tests.js
 create mode 100644 test/jio/tests.js

diff --git a/test/jio.storage/localstorage.tests.js b/test/jio.storage/localstorage.tests.js
new file mode 100644
index 0000000..5a7011a
--- /dev/null
+++ b/test/jio.storage/localstorage.tests.js
@@ -0,0 +1,561 @@
+/*jslint indent: 2, maxlen: 80, nomen: true */
+/*global define, jIO, jio_tests, window, test, ok, deepEqual, sinon, expect */
+
+// define([module_name], [dependencies], module);
+(function (dependencies, module) {
+  "use strict";
+  if (typeof define === 'function' && define.amd) {
+    return define(dependencies, module);
+  }
+  module(jIO, jio_tests);
+}(['jio', 'jio_tests', 'localstorage'], function (jIO, util) {
+  "use strict";
+
+  function generateTools() {
+    return {
+      clock: sinon.useFakeTimers(),
+      spy: util.ospy,
+      tick: util.otick
+    };
+  }
+
+  module("LocalStorage");
+
+  test("Post", function () {
+    expect(5);
+    var clock = sinon.useFakeTimers(), jio = jIO.newJio({
+      "type": "local",
+      "username": "upost",
+      "application_name": "apost"
+    });
+
+    // post without id
+    jio.post({}, function (err, response) {
+      var uuid;
+      util.
+        spyJioCallback('jobstatus', 'done', "Post without id")(err, response);
+      uuid = (err || response).id;
+      ok(util.isUuid(uuid), "Uuid should look like " +
+         "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx : " + uuid);
+    });
+    clock.tick(1000);
+
+    // post non empty document
+    jio.post(
+      {"_id": "post1", "title": "myPost1"},
+      util.spyJioCallback("value", {"ok": true, "id": "post1"}, "Post")
+    );
+    clock.tick(1000);
+
+    deepEqual(
+      util.jsonlocalstorage.getItem("jio/localstorage/upost/apost/post1"),
+      {
+        "_id": "post1",
+        "title": "myPost1"
+      },
+      "Check document"
+    );
+
+    // post but document already exists
+    jio.post({"_id": "post1", "title": "myPost2"}, util.spyJioCallback(
+      "status",
+      409,
+      "Post but document already exists"
+    ));
+    clock.tick(1000);
+
+    util.closeAndcleanUpJio(jio);
+  });
+
+  test("Put", function () {
+    expect(5);
+    var clock = sinon.useFakeTimers(), jio = jIO.newJio({
+      "type": "local",
+      "username": "uput",
+      "application_name": "aput"
+    });
+
+    // put without id
+    // error 20 -> document id required
+    jio.put({}, util.spyJioCallback("status", 20, "Put without id"));
+    clock.tick(1000);
+
+    // put non empty document
+    jio.put({"_id": "put1", "title": "myPut1"}, util.spyJioCallback("value", {
+      "ok": true,
+      "id": "put1"
+    }, "Creates a document"));
+    clock.tick(1000);
+
+    // check document
+    deepEqual(
+      util.jsonlocalstorage.getItem("jio/localstorage/uput/aput/put1"),
+      {
+        "_id": "put1",
+        "title": "myPut1"
+      },
+      "Check document"
+    );
+
+    // put but document already exists
+    jio.put({"_id": "put1", "title": "myPut2"}, util.spyJioCallback("value", {
+      "ok": true,
+      "id": "put1"
+    }, "Update the document"));
+    clock.tick(1000);
+
+    // check document
+    deepEqual(
+      util.jsonlocalstorage.getItem("jio/localstorage/uput/aput/put1"),
+      {
+        "_id": "put1",
+        "title": "myPut2"
+      },
+      "Check document"
+    );
+
+    util.closeAndcleanUpJio(jio);
+  });
+
+  test("PutAttachment", function () {
+
+    var o = generateTools(this);
+
+    o.jio = jIO.newJio({
+      "type": "local",
+      "username": "uputattmt",
+      "application_name": "aputattmt"
+    });
+
+    // putAttachment without doc id
+    // error 20 -> document id required
+    o.spy(o, "status", 20, "PutAttachment without doc id");
+    o.jio.putAttachment({}, o.f);
+    o.tick(o);
+
+    // putAttachment without attachment id
+    // error 22 -> attachment id required
+    o.spy(o, "status", 22, "PutAttachment without attachment id");
+    o.jio.putAttachment({"_id": "putattmt1"}, o.f);
+    o.tick(o);
+
+    // putAttachment without document
+    // error 404 -> not found
+    o.spy(o, "status", 404, "PutAttachment without document");
+    o.jio.putAttachment({"_id": "putattmt1", "_attachment": "putattmt2"}, o.f);
+    o.tick(o);
+
+    // adding a document
+    util.jsonlocalstorage.setItem(
+      "jio/localstorage/uputattmt/aputattmt/putattmt1",
+      {
+        "_id": "putattmt1",
+        "title": "myPutAttmt1"
+      }
+    );
+
+    // putAttachment with document
+    o.spy(o, "value",
+          {"ok": true, "id": "putattmt1", "attachment": "putattmt2"},
+          "PutAttachment with document, without data");
+    o.jio.putAttachment({"_id": "putattmt1", "_attachment": "putattmt2"}, o.f);
+    o.tick(o);
+
+    // check document
+    deepEqual(
+      util.jsonlocalstorage.getItem(
+        "jio/localstorage/uputattmt/aputattmt/putattmt1"
+      ),
+      {
+        "_id": "putattmt1",
+        "title": "myPutAttmt1",
+        "_attachments": {
+          "putattmt2": {
+            "length": 0,
+            // md5("")
+            "digest": "md5-d41d8cd98f00b204e9800998ecf8427e"
+          }
+        }
+      },
+      "Check document"
+    );
+
+    // check attachment
+    deepEqual(util.jsonlocalstorage.getItem(
+      "jio/localstorage/uputattmt/aputattmt/putattmt1/putattmt2"
+    ), "", "Check attachment");
+
+    // update attachment
+    o.spy(o, "value",
+          {"ok": true, "id": "putattmt1", "attachment": "putattmt2"},
+          "Update Attachment, with data");
+    o.jio.putAttachment({
+      "_id": "putattmt1",
+      "_attachment": "putattmt2",
+      "_data": "abc"
+    }, o.f);
+    o.tick(o);
+
+    // check document
+    deepEqual(
+      util.jsonlocalstorage.getItem(
+        "jio/localstorage/uputattmt/aputattmt/putattmt1"
+      ),
+      {
+        "_id": "putattmt1",
+        "title": "myPutAttmt1",
+        "_attachments": {
+          "putattmt2": {
+            "length": 3,
+            // md5("abc")
+            "digest": "md5-900150983cd24fb0d6963f7d28e17f72"
+          }
+        }
+      },
+      "Check document"
+    );
+
+    // check attachment
+    deepEqual(util.jsonlocalstorage.getItem(
+      "jio/localstorage/uputattmt/aputattmt/putattmt1/putattmt2"
+    ), "abc", "Check attachment");
+
+    util.closeAndcleanUpJio(o.jio);
+  });
+
+  test("Get", function () {
+
+    var o = generateTools(this);
+
+    o.jio = jIO.newJio({
+      "type": "local",
+      "username": "uget",
+      "application_name": "aget"
+    });
+
+    // get inexistent document
+    o.spy(o, "status", 404, "Get inexistent document");
+    o.jio.get({"_id": "get1"}, o.f);
+    o.tick(o);
+
+    // get inexistent attachment
+    o.spy(o, "status", 404, "Get inexistent attachment");
+    o.jio.getAttachment({"_id": "get1", "_attachment": "get2"}, o.f);
+    o.tick(o);
+
+    // adding a document
+    o.doc_get1 = {
+      "_id": "get1",
+      "title": "myGet1"
+    };
+    util.jsonlocalstorage.setItem(
+      "jio/localstorage/uget/aget/get1",
+      o.doc_get1
+    );
+
+    // get document
+    o.spy(o, "value", o.doc_get1, "Get document");
+    o.jio.get({"_id": "get1"}, o.f);
+    o.tick(o);
+
+    // get inexistent attachment (document exists)
+    o.spy(o, "status", 404, "Get inexistent attachment (document exists)");
+    o.jio.getAttachment({"_id": "get1", "_attachment": "get2"}, o.f);
+    o.tick(o);
+
+    // adding an attachment
+    o.doc_get1._attachments = {
+      "get2": {
+        "length": 2,
+        // md5("de")
+        "digest": "md5-5f02f0889301fd7be1ac972c11bf3e7d"
+      }
+    };
+    util.jsonlocalstorage.setItem(
+      "jio/localstorage/uget/aget/get1",
+      o.doc_get1
+    );
+    util.jsonlocalstorage.setItem("jio/localstorage/uget/aget/get1/get2", "de");
+
+    // get attachment
+    o.spy(o, "value", "de", "Get attachment");
+    o.jio.getAttachment({"_id": "get1", "_attachment": "get2"}, o.f);
+    o.tick(o);
+
+    util.closeAndcleanUpJio(o.jio);
+  });
+
+  test("Remove", function () {
+
+    var o = generateTools(this);
+
+    o.jio = jIO.newJio({
+      "type": "local",
+      "username": "uremove",
+      "application_name": "aremove"
+    });
+
+    // remove inexistent document
+    o.spy(o, "status", 404, "Remove inexistent document");
+    o.jio.remove({"_id": "remove1"}, o.f);
+    o.tick(o);
+
+    // remove inexistent attachment
+    o.spy(o, "status", 404, "Remove inexistent attachment");
+    o.jio.removeAttachment({"_id": "remove1", "_attachment": "remove2"}, o.f);
+    o.tick(o);
+
+    // adding a document + attmt
+    util.jsonlocalstorage.setItem("jio/localstorage/uremove/aremove/remove1", {
+      "_id": "remove1",
+      "title": "myRemove1",
+      "_attachments": {
+        "remove2": {
+          "length": 4,
+          "digest": "md5-blahblah"
+        }
+      }
+    });
+    util.jsonlocalstorage.setItem(
+      "jio/localstorage/uremove/aremove/remove1/remove2",
+      "fghi"
+    );
+
+    // remove attachment
+    o.spy(o, "value", {"ok": true, "id": "remove1", "attachment": "remove2"},
+          "Remove document");
+    o.jio.removeAttachment({"_id": "remove1", "_attachment": "remove2"}, o.f);
+    o.tick(o);
+
+    // remove document
+    o.spy(o, "value", {"ok": true, "id": "remove1"}, "Remove document");
+    o.jio.remove({"_id": "remove1"}, o.f);
+    o.tick(o);
+
+    // check document
+    ok(util.jsonlocalstorage.getItem(
+      "jio/localstorage/uremove/aremove/remove1"
+    ) === null, "Check document is removed");
+
+    // adding a document + attmt
+    util.jsonlocalstorage.setItem("jio/localstorage/uremove/aremove/remove1", {
+      "_id": "remove1",
+      "title": "myRemove1",
+      "_attachments": {
+        "remove2": {
+          "length": 4,
+          "digest": "md5-blahblah"
+        }
+      }
+    });
+    util.jsonlocalstorage.setItem(
+      "jio/localstorage/uremove/aremove/remove1/remove2", "fghi");
+
+    // remove attachment
+    o.spy(o, "value", {"ok": true, "id": "remove1"},
+          "Remove document and attachment");
+    o.jio.remove({"_id": "remove1"}, o.f);
+    o.tick(o);
+    ok(util.jsonlocalstorage.getItem(
+      "jio/localstorage/uremove/aremove/remove1"
+    ) === null, "Check document is removed");
+    ok(util.jsonlocalstorage.getItem(
+      "jio/localstorage/uremove/aremove/remove1/remove2"
+    ) === null, "Check attachment is removed");
+
+    util.closeAndcleanUpJio(o.jio);
+  });
+
+  test("AllDocs", function () {
+
+    var o = generateTools(this), i, m = 15;
+
+    o.jio = jIO.newJio({
+      "type": "local",
+      "username": "ualldocs",
+      "application_name": "aalldocs"
+    });
+    o.localpath = "jio/localstorage/ualldocs/aalldocs";
+
+    // sample data
+    o.titles = [
+      "Shawshank Redemption",
+      "Godfather",
+      "Godfather 2",
+      "Pulp Fiction",
+      "The Good, The Bad and The Ugly",
+      "12 Angry Men",
+      "The Dark Knight",
+      "Schindlers List",
+      "Lord of the Rings - Return of the King",
+      "Fight Club",
+      "Star Wars Episode V",
+      "Lord Of the Rings - Fellowship of the Ring",
+      "One flew over the Cuckoo's Nest",
+      "Inception",
+      "Godfellas"
+    ];
+    o.years = [
+      1994,
+      1972,
+      1974,
+      1994,
+      1966,
+      1957,
+      2008,
+      1993,
+      2003,
+      1999,
+      1980,
+      2001,
+      1975,
+      2010,
+      1990
+    ];
+    o.director = [
+      "Frank Darabont",
+      "Francis Ford Coppola",
+      "Francis Ford Coppola",
+      "Quentin Tarantino",
+      "Sergio Leone",
+      "Sidney Lumet",
+      "Christopher Nolan",
+      "Steven Spielberg",
+      "Peter Jackson",
+      "David Fincher",
+      "Irvin Kershner",
+      "Peter Jackson",
+      "Milos Forman",
+      "Christopher Nolan",
+      "Martin Scorsese"
+    ];
+
+    for (i = 0; i < m; i += 1) {
+      o.fakeDoc = {};
+      o.fakeDoc._id = "doc_"+(i < 10 ? "0"+i : i);
+      o.fakeDoc.title = o.titles[i];
+      o.fakeDoc.year = o.years[i];
+      o.fakeDoc.author = o.director[i];
+      if (i === 5) {
+        o.fakeDoc._attachments = {
+          "att": {
+            "digest": "md5-dontcare",
+            "content_type": "text/plain",
+            "length": 3
+          }
+        };
+        util.jsonlocalstorage.setItem(o.localpath + "/doc_05/att", "abc");
+      }
+      util.jsonlocalstorage.setItem(
+        o.localpath+"/doc_"+(i < 10 ? "0"+i : i),
+        o.fakeDoc
+      );
+    }
+
+    // response
+    o.allDocsResponse = {};
+    o.allDocsResponse.rows = [];
+    o.allDocsResponse.total_rows = 15;
+    for (i = 0; i < m; i += 1) {
+      o.allDocsResponse.rows.push({
+        "id": "doc_"+(i < 10 ? "0"+i : i),
+        "key": "doc_"+(i < 10 ? "0"+i : i),
+        "value": {}
+      });
+    }
+    // alldocs
+    o.spy(o, "value", o.allDocsResponse, "All docs");
+    o.jio.allDocs(function (err, response) {
+      if (response && response.rows) {
+        response.rows.sort(function (a, b) {
+          return a.id > b.id ? 1 : a.id < b.id ? -1 : 0;
+        });
+      }
+      o.f(err, response);
+    });
+    o.tick(o);
+
+    // include docs
+    o.allDocsResponse = {};
+    o.allDocsResponse.rows = [];
+    o.allDocsResponse.total_rows = m;
+    for (i = 0; i < m; i += 1) {
+      o.allDocsResponse.rows.push({
+        "id": "doc_"+(i < 10 ? "0"+i : i),
+        "key": "doc_"+(i < 10 ? "0"+i : i),
+        "value": {},
+        "doc": util.jsonlocalstorage.getItem(
+          o.localpath+"/doc_"+(i < 10 ? "0"+i : i)
+        )
+      });
+    }
+
+    // alldocs
+    o.spy(o, "value", o.allDocsResponse, "All docs (include docs)");
+    o.jio.allDocs({"include_docs": true}, function (err, response) {
+      if (response && response.rows) {
+        response.rows.sort(function (a, b) {
+          return a.id > b.id ? 1 : a.id < b.id ? -1 : 0;
+        });
+      }
+      o.f(err, response);
+    });
+    o.tick(o);
+
+    // complex queries
+    o.thisShouldBeTheAnswer4 = {"total_rows": 0, "rows": []};
+    o.allDocsResponse.rows.forEach(function (row) {
+      var new_row;
+      if (row.doc.year >= 1980) {
+        new_row = JSON.parse(JSON.stringify(row));
+        new_row.value.title = row.doc.title;
+        new_row.value.year = row.doc.year;
+        delete new_row.doc;
+        o.thisShouldBeTheAnswer4.rows.push(new_row);
+        o.thisShouldBeTheAnswer4.total_rows += 1;
+      }
+    });
+    o.thisShouldBeTheAnswer4.rows.sort(function (a, b) {
+      return a.value.year > b.value.year ? -1 :
+        a.value.year < b.value.year ? 1 : 0;
+    });
+    o.thisShouldBeTheAnswer4.total_rows = 5;
+    o.thisShouldBeTheAnswer4.rows.length = 5;
+
+    o.spy(o, "value", o.thisShouldBeTheAnswer4,
+          "allDocs (complex queries year >= 1980, all query options)");
+    o.jio.allDocs({
+      "query": '(year: >= "1980")',
+      "limit": [0,5],
+      "sort_on": [["year", "descending"]],
+      "select_list": ["title", "year"]
+    }, o.f);
+    o.tick(o);
+
+    // empty query returns all
+    o.thisShouldBeTheAnswer5 = {"total_rows": 0, "rows": []};
+    o.allDocsResponse.rows.forEach(function (row) {
+      var new_row = JSON.parse(JSON.stringify(row));
+      new_row.value.title = row.doc.title;
+      o.thisShouldBeTheAnswer5.rows.push(new_row);
+      o.thisShouldBeTheAnswer5.total_rows += 1;
+    });
+    o.thisShouldBeTheAnswer5.rows.sort(function (a, b) {
+      return a.value.title > b.value.title ? -1 :
+        a.value.title < b.value.title ? 1 : 0;
+    });
+
+    o.spy(o, "value", o.thisShouldBeTheAnswer5,
+          "allDocs (empty query in complex query)");
+
+    o.jio.allDocs({
+      "sort_on": [["title", "descending"]],
+      "select_list": ["title"],
+      "include_docs": true
+    }, o.f);
+    o.tick(o);
+
+    util.closeAndcleanUpJio(o.jio);
+  });
+
+}));
diff --git a/test/jio/tests.js b/test/jio/tests.js
new file mode 100644
index 0000000..1b7bb58
--- /dev/null
+++ b/test/jio/tests.js
@@ -0,0 +1,746 @@
+/*jslint indent: 2, maxlen: 80, nomen: true */
+/*global define, jIO, localStorage, window, test, ok, deepEqual, sinon,
+  expect */
+
+// define([module_name], [dependencies], module);
+(function (dependencies, module) {
+  "use strict";
+  if (typeof define === 'function' && define.amd) {
+    return define(dependencies, module);
+  }
+  window.jio_tests = module(jIO);
+}(['jio'], function (jIO) {
+  "use strict";
+  var to_export = {}, tmp;
+
+  // localStorage cleanup
+  for (tmp in localStorage) {
+    if (localStorage.hasOwnProperty(tmp)) {
+      if (/^jio\//.test(tmp)) {
+        localStorage.removeItem(tmp);
+      }
+    }
+  }
+
+  //////////////////////////////////////////////////////////////////////////////
+  // Tools
+
+  function spyJioCallback(result_type, value, message) {
+    return function (err, response) {
+      var val;
+      switch (result_type) {
+      case 'value':
+        val = err || response;
+        break;
+      case 'status':
+        val = (err || {}).status;
+        break;
+      case 'jobstatus':
+        val = (err ? 'fail' : 'done');
+        break;
+      default:
+        ok(false, "Unknown case " + result_type);
+        break;
+      }
+      deepEqual(val, value, message);
+    };
+  }
+  to_export.spyJioCallback = spyJioCallback;
+
+  /**
+   * Clones all native object in deep. Managed types: Object, Array, String,
+   * Number, Boolean, Function, null.
+   *
+   * @param  {A} object The object to clone
+   * @return {A} The cloned object
+   */
+  function deepClone(object) {
+    var i, cloned;
+    if (Array.isArray(object)) {
+      cloned = [];
+      for (i = 0; i < object.length; i += 1) {
+        cloned[i] = deepClone(object[i]);
+      }
+      return cloned;
+    }
+    if (typeof object === "object") {
+      cloned = {};
+      for (i in object) {
+        if (object.hasOwnProperty(i)) {
+          cloned[i] = deepClone(object[i]);
+        }
+      }
+      return cloned;
+    }
+    return object;
+  }
+
+  // XXX docstring
+  function isUuid(uuid) {
+    var x = "[0-9a-fA-F]";
+    if (typeof uuid !== "string") {
+      return false;
+    }
+    return (uuid.match(
+      "^" + x + "{8}-" + x + "{4}-" +
+        x + "{4}-" + x + "{4}-" + x + "{12}$"
+    ) === null ? false : true);
+  }
+  to_export.isUuid = isUuid;
+
+  // XXX docstring
+  to_export.jsonlocalstorage = {
+    clear: function () {
+      return localStorage.clear();
+    },
+    getItem: function (item) {
+      var value = localStorage.getItem(item);
+      return value === null ? null : JSON.parse(value);
+    },
+    setItem: function (item, value) {
+      return localStorage.setItem(item, JSON.stringify(value));
+    },
+    removeItem: function (item) {
+      return localStorage.removeItem(item);
+    }
+  };
+
+  function objectifyDocumentArray(array) {
+    var obj = {}, k;
+    for (k = 0; k < array.length; k += 1) {
+      obj[array[k]._id] = array[k];
+    }
+    return obj;
+  }
+
+  function closeAndcleanUpJio(jio) {
+    jio.close();
+    to_export.jsonlocalstorage.removeItem("jio/id/" + jio.getId());
+    to_export.jsonlocalstorage.removeItem("jio/job_array/" + jio.getId());
+  }
+  to_export.closeAndcleanUpJio = closeAndcleanUpJio;
+
+  function getJioLastJob(jio) {
+    return (to_export.jsonlocalstorage.getItem(
+      "jio/job_array/" + jio.getId()
+    ) || [undefined]).pop();
+  }
+
+  //////////////////////////////////////////////////////////////////////////////
+  // Compatibility
+
+  function ospy(o, result_type, value, message, function_name) {
+    function_name = function_name || 'f';
+    o[function_name] = function (err, response) {
+      var val;
+      switch (result_type) {
+      case 'value':
+        val = err || response;
+        break;
+      case 'status':
+        val = (err || {}).status;
+        break;
+      case 'jobstatus':
+        val = (err ? 'fail' : 'done');
+        break;
+      default:
+        ok(false, "Unknown case " + result_type);
+        break;
+      }
+      deepEqual(val, value, message);
+    };
+    sinon.spy(o, function_name);
+  }
+  to_export.ospy = ospy;
+
+  function otick(o, a, b) {
+    var tick = 10000, function_name = 'f';
+    if (typeof a === 'number' && !isNaN(a)) {
+      tick = a;
+      a = b;
+    }
+    if (typeof a === 'string') {
+      function_name = a;
+    }
+    o.clock.tick(tick);
+    if (!o[function_name].calledOnce) {
+      if (o[function_name].called) {
+        ok(false, 'too much results');
+      } else {
+        ok(false, 'no response');
+      }
+    }
+  }
+  to_export.otick = otick;
+
+  //////////////////////////////////////////////////////////////////////////////
+  // Dummy Storage
+
+  // XX docstring
+  function dummyStorage(spec, my) {
+    var that = my.basicStorage(spec, my);
+
+    that._mode = spec.mode || 'normal';
+    that._key = spec.key;
+    that._value = spec.value;
+
+    if (that._mode === 'spec error') {
+      throw new TypeError(
+        "Initialization error set by the storage description."
+      );
+    }
+
+    that.specToStore = function () {
+      return {
+        "mode": that._mode,
+        "key": that._key,
+        "value": that._value
+      };
+    };
+
+    that.post = function (command) {
+      setTimeout(function () {
+        var metadata = command.cloneDoc(), options = command.cloneOption();
+        if (that._mode === 'no response') {
+          return;
+        }
+        // if (that._mode === 'no response + timeout reset') {
+        //   return setTimeout(function () {
+        //     command.resetTimeout();
+        //   }, that._value);
+        // }
+        if (that._mode === 'invalid error response') {
+          return that.error();
+        }
+        if (that._mode === 'always fail') {
+          return that.error({
+            "error": "conflict",
+            "message": "",
+            "reason": "unknown",
+            "status": 409,
+            "statusText": "Conflict"
+          });
+        }
+        if (that._mode === 'post response no id') {
+          return that.success();
+        }
+        that.success({"id": "document id a", "ok": true});
+      });
+    };
+
+    that.put = function (command) {
+      setTimeout(function () {
+        var metadata = command.cloneDoc(), options = command.cloneOption();
+        if (that._mode === 'retry') {
+          if (!dummyStorage[that._key]) {
+            dummyStorage[that._key] = 0;
+          }
+          dummyStorage[that._key] += 1;
+          if (dummyStorage[that._key] === that._value) {
+            return that.success({"id": metadata._id, "ok": true});
+          }
+          return that.retry();
+        }
+        that.success({"id": metadata._id, "ok": true});
+      });
+    };
+
+    that.remove = function (command) {
+      setTimeout(function () {
+        var metadata = command.cloneDoc(), options = command.cloneOption();
+        that.success({"id": metadata._id, "ok": true});
+      });
+    };
+
+    return that;
+  }
+
+  jIO.addStorageType('dummy', dummyStorage);
+  to_export.dummyStorage = dummyStorage;
+
+  //////////////////////////////////////////////////////////////////////////////
+  // Tests
+
+  module("JIO");
+
+  /**
+   * Tests the instance initialization
+   */
+  test('Instanciation', function () {
+    expect(1);
+    var jio = jIO.newJio(undefined);
+
+    // tests if jio is an object
+    ok(typeof jio === 'object', 'Init ok!');
+
+    // checks the workspace
+    // XXX nothing to check for the moment, need to be implemented first
+
+    closeAndcleanUpJio(jio);
+  });
+
+  module("JIO Dummy Storage");
+
+  // XXX This will be uncommented when given parameters checking will be implem
+  // /**
+  //  * Tests wrong commands
+  //  */
+  // test('Wrong parameters', function () {
+  //   var result, jio = jIO.newJio({
+  //     "type": "dummy",
+  //     "mode": "normal"
+  //   }, {
+  //     "workspace": {}
+  //   });
+
+  //   try {
+  //     jio.post(); // post(kwargs, [options], [callbacks]);
+  //     result = "No error thrown";
+  //   } catch (e1) {
+  //     result = e1.name + ": " + e1.message;
+  //   }
+  //   deepEqual(
+  //     result,
+  //     "TypeError: JIO().post(): Argument 1 is not of type 'object'",
+  //     "Wrong parameter"
+  //   );
+
+  //   try {
+  //     jio.allDocs(); // allDocs([options], [callbacks]);
+  //     result = "No error thrown";
+  //   } catch (e2) {
+  //     result = e2.name + ": " + e2.message;
+  //   }
+  //   deepEqual(result, "No error thrown", "Good parameter");
+  // });
+
+  // XXX this will be uncommented when validateState will be replaced by a
+  //     simple `throw` in the storage init
+  // /**
+  //  * Tests a storage initialization error
+  //  */
+  // test('Description Error', function () {
+  //   var clock, jio;
+  //   clock = sinon.useFakeTimers();
+  //   jio = jIO.newJio({
+  //     "type": "blue"
+  //   }, {
+  //     "workspace": {}
+  //   });
+
+  //   // Tests wrong storage type
+  //   jio.post({}).always(function (answer) {
+  //     deepEqual(answer, {
+  //       "error": "internal_storage_error",
+  //       "message": "Check if the storage description respects the " +
+  //         "constraints provided by the storage designer. (TypeError: " +
+  //         "Unknown storage 'blue')",
+  //       "reason": "invalid description",
+  //       "status": 551,
+  //       "statusText": "Internal Storage Error"
+  //     }, "Unknown storage");
+  //   });
+  //   clock.tick(1);
+
+  //   // Tests wrong storage description
+  //   jio = jIO.newJio({
+  //     "type": "dummy",
+  //     "mode": "spec error"
+  //   }, {
+  //     "workspace": {}
+  //   });
+
+  //   jio.post({}).always(function (answer) {
+  //     deepEqual(answer, {
+  //       "error": "internal_storage_error",
+  //       "message": "Check if the storage description respects the " +
+  //         "constraints provided by the storage designer. (TypeError: " +
+  //         "Initialization error set by the storage description.)",
+  //       "reason": "invalid description",
+  //       "status": 551,
+  //       "statusText": "Internal Storage Error"
+  //     }, "Initialization error");
+  //   });
+  //   clock.tick(1);
+  // });
+
+
+  // XXX timeout is not implemented yet
+  // /**
+  //  * Tests a command which does not respond
+  //  */
+  // test('No Response or Response Timeout', function () {
+  //   var clock, jio, state;
+  //   expect(5);
+  //   clock = sinon.useFakeTimers();
+  //   jio = jIO.newJio({
+  //     "type": "dummy",
+  //     "mode": "no response"
+  //   }, {
+  //     "workspace": {}
+  //   });
+
+  //   // tests with default timeout
+  //   jio.post({}).always(function (answer) {
+  //     deepEqual(answer, {
+  //       "error": "request_timeout",
+  //       "message": "Operation canceled after around 10000 milliseconds.",
+  //       "reason": "timeout",
+  //       "status": 408,
+  //       "statusText": "Request Timeout"
+  //     }, "Timeout error (default timeout)");
+  //   });
+  //   clock.tick(10000); // wait 10 seconds
+
+  //   jio = jIO.newJio({
+  //     "type": "dummy",
+  //     "mode": "no response + timeout reset",
+  //     "value": 5000 // reset after 5 seconds
+  //   }, {
+  //     "workspace": {}
+  //   });
+
+  //   // tests with storage timeout extension
+  //   state = "Not called yet";
+  //   jio.post({}).always(function (answer) {
+  //     state = "Called";
+  //     deepEqual(answer, {
+  //       "error": "request_timeout",
+  //       "message": "Operation canceled after around 15000 milliseconds.",
+  //       "reason": "timeout",
+  //       "status": 408,
+  //       "statusText": "Request Timeout"
+  //     }, "Timeout error (storage timeout reset)");
+  //   });
+  //   clock.tick(10000); // wait 10 seconds
+  //   deepEqual(state, "Not called yet", "Check callback state.");
+  //   clock.tick(5000); // wait 5 seconds
+
+  //   jio = jIO.newJio({
+  //     "type": "dummy",
+  //     "mode": "no response"
+  //   }, {
+  //     "workspace": {},
+  //     "default_timeout": 2
+  //   });
+
+  //   // tests with jio option timeout
+  //   jio.post({}).always(function (answer) {
+  //     deepEqual(answer, {
+  //       "error": "request_timeout",
+  //       "message": "Operation canceled after around 2 milliseconds.",
+  //       "reason": "timeout",
+  //       "status": 408,
+  //       "statusText": "Request Timeout"
+  //     }, "Timeout error (specific default timeout)");
+  //   });
+  //   clock.tick(2);
+
+  //   // tests with command option timeout
+  //   jio.post({}, {"timeout": 50}).always(function (answer) {
+  //     deepEqual(answer, {
+  //       "error": "request_timeout",
+  //       "message": "Operation canceled after around 50 milliseconds.",
+  //       "reason": "timeout",
+  //       "status": 408,
+  //       "statusText": "Request Timeout"
+  //     }, "Timeout error (command timeout)");
+  //   });
+  //   clock.tick(50);
+  // });
+
+  // /**
+  //  * Tests wrong responses
+  //  */
+  // test('Invalid Response', function () {
+  //   var clock, jio;
+  //   clock = sinon.useFakeTimers();
+  //   jio = jIO.newJio({
+  //     "type": "dummy",
+  //     "mode": "post response no id"
+  //   });
+
+  //   jio.post({}, function (err, response) {
+  //     deepEqual(err || response, {
+  //       "error": "internal_storage_error",
+  //       "message": "New document id have to be specified",
+  //       "reason": "invalid response",
+  //       "status": 551,
+  //       "statusText": "Internal Storage Error"
+  //     }, "Invalid Post Response");
+  //   });
+  //   clock.tick(1000);
+
+  //   closeAndcleanUpJio(jio);
+
+  //   jio = jIO.newJio({
+  //     "type": "dummy",
+  //     "mode": "invalid error response"
+  //   });
+
+  //   jio.post({}, function (err, response) {
+  //     deepEqual(err || response, {
+  //       "error": "internal_storage_error",
+  //       "message": "Unknown status \"undefined\"",
+  //       "reason": "invalid response",
+  //       "status": 551,
+  //       "statusText": "Internal Storage Error"
+  //     }, "Invalid Post Error Response");
+  //   });
+  //   clock.tick(1000);
+
+  //   closeAndcleanUpJio(jio);
+  // });
+
+  /**
+   * Tests a valid responses
+   */
+  test('Valid Responses & Callbacks', function () {
+    expect(4);
+    var clock, jio, message;
+    clock = sinon.useFakeTimers();
+
+    jio = jIO.newJio({
+      "type": "dummy",
+      "mode": "normal"
+    });
+
+    // Tests post command callbacks post(metadata).always(onResponse) +
+    // valid response.
+    message = "Post Command: post(metadata).always(function (answer) {..}) + " +
+      "valid response.";
+    jio.post({}, function (err, response) {
+      deepEqual(err || response, {
+        "ok": true,
+        "id": "document id a"
+      }, message);
+    });
+    clock.tick(1000);
+
+    // Tests post command callbacks post(metadata).done(onSuccess).fail(onError)
+    message = "Post Command: post(metadata).done(function (answer) {..})." +
+      "fail(function (answer) {..})";
+    jio.post({}, function (answer) {
+      deepEqual(answer, {
+        "ok": true,
+        "id": "document id a"
+      }, message);
+    }, function (answer) {
+      deepEqual(answer, "Should not fail", message);
+    });
+    clock.tick(1000);
+
+    // Tests post command callbacks post(metadata, onResponse)
+    message = "Post Command: post(metadata, function (err, response) {..})";
+    jio.post({}, function (err, response) {
+      if (err) {
+        return deepEqual(err, "Should not fail", message);
+      }
+      deepEqual(response, {
+        "ok": true,
+        "id": "document id a"
+      }, message);
+    });
+    clock.tick(1000);
+
+    closeAndcleanUpJio(jio);
+
+    // Tests post command callbacks post(metadata, onSuccess, onError) + error
+    // response.
+    message = "Post Command: post(metadata, function (response) {..}, " +
+      "function (err) {..}) + valid error response.";
+    jio = jIO.newJio({
+      "type": "dummy",
+      "mode": "always fail"
+    });
+
+    jio.post({}, function (response) {
+      deepEqual(response, "Should fail", message);
+    }, function (err) {
+      deepEqual(err, {
+        "status": 409,
+        "statusText": "Conflict",
+        "error": "conflict",
+        "reason": "unknown",
+        "message": ""
+      }, message);
+    });
+    clock.tick(1000);
+
+    closeAndcleanUpJio(jio);
+  });
+
+  module("JIO Job Management");
+
+  test("Several Jobs at the same time", function () {
+    expect(3);
+    var clock = sinon.useFakeTimers(), jio = jIO.newJio({"type": "dummy"});
+
+    jio.put({"_id": "file1",  "title": "t1"}, spyJioCallback('value', {
+      "ok": true,
+      "id": "file1"
+    }, "job1"));
+    jio.put({"_id": "file2", "title": "t2"}, spyJioCallback('value', {
+      "ok": true,
+      "id": "file2"
+    }, "job2"));
+    jio.put({"_id": "file3", "title": "t3"}, spyJioCallback('value', {
+      "ok": true,
+      "id": "file3"
+    }, "job3"));
+    clock.tick(1000);
+
+    closeAndcleanUpJio(jio);
+  });
+
+  test("Similar Jobs at the same time (Update)", function () {
+    expect(8);
+    var clock = sinon.useFakeTimers(), jio = jIO.newJio({"type": "dummy"});
+    function compareResults(err, response) {
+      deepEqual(err || response, {"id": "file", "ok": true}, "job ok");
+    }
+    jio.put({"_id": "file", "title": "t"}, compareResults);
+    jio.put({"_id": "file", "title": "t"}, compareResults);
+    jio.put({"_id": "file", "title": "t"}, compareResults);
+    deepEqual(getJioLastJob(jio).id, 1, "Check job queue");
+    clock.tick(1000);
+
+    jio.put({"_id": "file", "content": "content"}, compareResults);
+    jio.remove({"_id": "file", "content": "content"}, compareResults);
+    jio.put({"_id": "file", "content": "content"}, compareResults);
+    deepEqual(getJioLastJob(jio).id, 5, "Check job queue");
+    clock.tick(10000);
+
+    closeAndcleanUpJio(jio);
+  });
+
+  test("Same document jobs at the same time (Wait for job(s))", function () {
+    expect(6);
+    var clock = sinon.useFakeTimers(), jio = jIO.newJio({"type": "dummy"});
+
+    function compareResults(err, response) {
+      deepEqual(err || response, {"id": "file", "ok": true}, "job ok");
+    }
+
+    jio.put({"_id": "file", "content": "content"}, compareResults);
+    deepEqual(
+      getJioLastJob(jio).status.waitforjob,
+      undefined,
+      "Job 1 is not waiting for someone"
+    );
+
+    jio.remove({"_id": "file", "content": "content"}, compareResults);
+    deepEqual(
+      getJioLastJob(jio).status.waitforjob,
+      [1],
+      "Job 2 is wainting for 1"
+    );
+
+    jio.put({"_id": "file"}, compareResults);
+    deepEqual(
+      getJioLastJob(jio).status.waitforjob,
+      [1, 2],
+      "Job 3 is waiting for 1 and 2"
+    );
+
+    clock.tick(1000);
+
+    closeAndcleanUpJio(jio);
+  });
+
+  test("Server will be available soon (Wait for time)", function () {
+    expect(2);
+    var clock = sinon.useFakeTimers(), jio;
+    jio = jIO.newJio({
+      "type": "dummy",
+      "mode": "retry",
+      "key": "035139054",
+      "value": 3
+    });
+    jio.put(
+      {"_id": "file", "content": "content"},
+      {"max_retry": 3},
+      function (err, response) {
+        deepEqual(err || response, {"id": "file", "ok": true}, "Job ok");
+      }
+    );
+    clock.tick(2000);
+
+    deepEqual(dummyStorage['035139054'], 3, "tried 3 times");
+    delete dummyStorage['035139054'];
+
+    closeAndcleanUpJio(jio);
+  });
+
+  test("Restore old Jio", function () {
+
+    var o = {
+      clock: sinon.useFakeTimers(),
+      spy: ospy,
+      tick: otick
+    };
+
+    function waitUntilLastJobIs(state) {
+      while (true) {
+        if (getJioLastJob(o.jio) === undefined) {
+          ok(false, "No job have state: " + state);
+          break;
+        }
+        if (getJioLastJob(o.jio).status.label === state) {
+          break;
+        }
+        o.clock.tick(25);
+      }
+    }
+
+    function waitUntilAJobExists(timeout) {
+      var cpt = 0, job = false;
+      while (true) {
+        if (getJioLastJob(o.jio) !== undefined) {
+          job = true;
+          break;
+        }
+        if (cpt >= timeout) {
+          break;
+        }
+        o.clock.tick(25);
+        cpt += 25;
+      }
+      ok(job, "Wait until a job is created");
+    }
+
+    o.jio = jIO.newJio({
+      "type": "dummy",
+      "mode": "retry",
+      "key": "12314",
+      "value": 3
+    });
+
+    o.jio_id = o.jio.getId();
+
+    o.jio.put({"_id": "file", "title": "myFile"}, {"max_retry":3}, o.f);
+    waitUntilLastJobIs("initial"); // "on going" or "wait" should work
+    // xxx also test with waitUntilLastJobIs("on going") ?
+    o.jio.close();
+
+    o.jio = jIO.newJio({
+      "type": "dummy",
+      "mode": "retry",
+      "key": "12314",
+      "value": 3
+    });
+    waitUntilAJobExists(30000); // timeout 30 sec
+
+    deepEqual(getJioLastJob(o.jio).command.label, 'put', 'Job restored');
+    o.clock.tick(2000);
+    ok(getJioLastJob(o.jio) === undefined, "Job executed");
+    o.clock.tick(1000);
+
+    to_export.jsonlocalstorage.removeItem("jio/id/" + o.jio_id);
+    to_export.jsonlocalstorage.removeItem("jio/job_array/" + o.jio_id);
+    closeAndcleanUpJio(o.jio);
+
+  });
+
+  return to_export;
+}));
diff --git a/test/jiotests.js b/test/jiotests.js
index 7ea47e5..031be94 100644
--- a/test/jiotests.js
+++ b/test/jiotests.js
@@ -401,59 +401,6 @@ test("Additional Filters", function () {
   ], 'The first document should be kept');
 });
 
-module ('Jio Global tests');
-
-test ( "Jio simple methods", function () {
-    // Test Jio simple methods
-    // It checks if we can create several instance of jio at the same
-    // time. Checks if they don't overlap informations, if they are
-    // started and stopped correctly and if they are ready when they
-    // have to be ready.
-
-    var o = generateTools(this);
-
-    o.jio = JIO.newJio();
-    ok ( o.jio, 'a new jio -> 1');
-
-    o.jio2 = JIO.newJio();
-    ok ( o.jio2, 'another new jio -> 2');
-
-    JIO.addStorageType('qunit', empty_fun);
-
-    ok ( o.jio2.getId() !== o.jio.getId(), '1 and 2 must be different');
-
-    o.jio.stop();
-    o.jio2.stop();
-
-});
-
-// test ( 'Jio Publish/Sububscribe/Unsubscribe methods', function () {
-//     // Test the Publisher, Subscriber of a single jio.
-//     // It is just testing if these function are working correctly.
-//     // The test publishes an event, waits a little, and check if the
-//     // event has been received by the callback of the previous
-//     // subscribe. Then, the test unsubscribe the callback function from
-//     // the event, and publish the same event. If it receives the event,
-//     // the unsubscribe method is not working correctly.
-
-//     var o = {};
-//     o.jio = JIO.newJio();
-
-//     var spy1 = this.spy();
-
-//     // Subscribe the pubsub_test event.
-//     o.callback = o.jio.subscribe('pubsub_test',spy1);
-//     // And publish the event.
-//     o.jio.publish('pubsub_test');
-//     ok (spy1.calledOnce, 'subscribing & publishing, event called once');
-
-//     o.jio.unsubscribe('pubsub_test',spy1);
-//     o.jio.publish('pubsub_test');
-//     ok (spy1.calledOnce, 'unsubscribing, same event not called twice');
-
-//     o.jio.stop();
-// });
-
 module ( "Jio Dummy Storages" );
 
 test ("All requests ok", function () {
@@ -752,636 +699,6 @@ test ("All document found", function () {
     o.jio.stop();
 });
 
-module ( "Jio Job Managing" );
-
-test ("Several Jobs at the same time", function () {
-
-    var o = generateTools(this);
-
-    o.jio = JIO.newJio({"type":"dummyallok"});
-    o.spy(o, "value", {"ok": true, "id": "file"}, "job1", "f");
-    o.spy(o, "value", {"ok": true, "id": "file2"}, "job2", "f2");
-    o.spy(o, "value", {"ok": true, "id": "file3"}, "job3", "f3");
-    o.jio.put({"_id": "file",  "content": "content"}, o.f);
-    o.jio.put({"_id": "file2", "content": "content2"}, o.f2);
-    o.jio.put({"_id": "file3", "content": "content3"}, o.f3);
-    o.tick(o, 1000, "f");
-    o.tick(o, "f2");
-    o.tick(o, "f3");
-    o.jio.stop();
-
-});
-
-test ("Similar Jobs at the same time (Update)", function () {
-
-    var o = generateTools(this);
-
-    o.jio = JIO.newJio({"type":"dummyallok"});
-    o.spy(o, "value", {"ok": true, "id": "file"}, "job1 ok", "f");
-    o.spy(o, "value", {"ok": true, "id": "file"}, "job2 ok", "f2");
-    o.spy(o, "value", {"ok": true, "id": "file"}, "job3 ok", "f3");
-    o.jio.put({"_id": "file", "content": "content"}, o.f); // 1
-    o.jio.put({"_id": "file", "content": "content"}, o.f2); // 2
-    o.jio.put({"_id": "file", "content": "content"}, o.f3); // 3
-    deepEqual(getLastJob(o.jio.getId()).id, 1, "Check job queue");
-    o.tick(o, 1000, "f");
-    o.tick(o, "f2");
-    o.tick(o, "f3");
-
-    o.spy(o, "value", {"ok": true, "id": "file"}, "job4 ok", "f");
-    o.spy(o, "value", {"ok": true, "id": "file"}, "job5 ok", "f2");
-    o.spy(o, "value", {"ok": true, "id": "file"}, "job6 ok", "f3");
-    o.jio.put({"_id": "file", "content": "content"}, o.f); // 4
-    o.jio.remove({"_id": "file", "content": "content"}, o.f2); // 5
-    o.jio.put({"_id": "file", "content": "content"}, o.f3); // 6
-    deepEqual(getLastJob(o.jio.getId()).id, 5, "Check job queue");
-    o.tick(o, 1000, "f");
-    o.tick(o, "f2");
-    o.tick(o, "f3");
-
-    o.jio.stop();
-
-});
-
-test ("One document aim jobs at the same time (Wait for job(s))" , function () {
-
-    var o = generateTools(this);
-
-    o.jio = JIO.newJio({"type":"dummyallok"});
-    o.spy(o, "value", {"ok": true, "id": "file"}, "job1", "f");
-    o.spy(o, "value", {"ok": true, "id": "file"}, "job2", "f2");
-    o.spy(o, "value", {"ok": true, "id": "file"}, "job3", "f3");
-
-    o.jio.put({"_id": "file", "content": "content"}, o.f);
-    o.testLastJobWaitForJob(undefined, "job1 is not waiting for someone");
-
-    o.jio.remove({"_id": "file", "content": "content"}, o.f2);
-    o.testLastJobWaitForJob([1], "job2 is waiting");
-
-    o.jio.put({"_id": "file"}, o.f3);
-    o.testLastJobWaitForJob([1, 2], "job3 is waiting");
-
-    o.tick(o, 1000, "f");
-    o.tick(o, "f2");
-    o.tick(o, "f3");
-
-    o.spy(o, "value", {"ok": true, "id": "lol"}, "Chaining callbacks");
-    o.jio.removeAttachment({
-      "_id": "lol",
-      "_attachment": "hu"
-    }, function (err, response) {
-      if (err) {
-        return;
-      }
-      o.jio.remove({"_id": "lol"}, o.f);
-    });
-    o.tick(o, 10000);
-
-    o.jio.stop();
-
-});
-
-test ("Server will be available soon (Wait for time)" , function () {
-
-    var o = generateTools(this);
-    o.max_retry = 3;
-
-    o.jio = JIO.newJio({"type":"dummyall3tries"});
-    o.spy(o, "value", {"ok": true, "id": "file"}, "job1", "f");
-
-    o.jio.put({"_id": "file", "content": "content"},
-              {"max_retry": o.max_retry}, o.f);
-    for (o.i = 0; o.i < o.max_retry - 1; o.i += 1) {
-        o.waitUntilLastJobIs("on going");
-        o.waitUntilLastJobIs("wait");
-        o.testLastJobWaitForTime("job1 is waiting for time");
-    }
-
-    o.tick(o, 1000, "f");
-    o.jio.stop();
-
-});
-
-module ( "Jio Restore");
-
-test ("Restore old Jio", function() {
-
-    var o = generateTools(this);
-
-    o.jio = JIO.newJio({
-        "type": "dummyall3tries",
-        "application_name": "jiotests"
-    });
-
-    o.jio_id = o.jio.getId();
-
-    o.jio.put({"_id": "file", "title": "myFile"}, {"max_retry":3}, o.f);
-    o.waitUntilLastJobIs("initial"); // "on going" or "wait" should work
-    // xxx also test with o.waitUntilLastJobIs("on going") ?
-    o.jio.close();
-
-    o.jio = JIO.newJio({
-        "type": "dummyallok",
-        "application_name": "jiotests"
-    });
-    o.waitUntilAJobExists(30000); // timeout 30 sec
-
-    o.testLastJobLabel("put", "Job restored");
-    o.clock.tick(2000);
-    ok(getLastJob(o.jio.getId()) === undefined, "Job executed");
-    o.clock.tick(1000);
-
-    o.jio.stop();
-
-});
-
-module ( "Jio LocalStorage" );
-
-test ("Post", function(){
-
-    var o = generateTools(this);
-
-    o.jio = JIO.newJio({
-        "type": "local",
-        "username": "upost",
-        "application_name": "apost"
-    });
-
-    // post without id
-    o.spy (o, "jobstatus", "done", "Post without id");
-    o.jio.post({}, function (err, response) {
-      var uuid;
-      o.f(err, response);
-      uuid = (err || response).id;
-      ok(isUuid(uuid), "Uuid should look like " +
-         "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx : " + uuid);
-    });
-    o.tick(o);
-
-    // post non empty document
-    o.spy (o, "value", {"ok": true, "id": "post1"}, "Post");
-    o.jio.post({"_id": "post1", "title": "myPost1"}, o.f);
-    o.tick(o);
-
-    deepEqual(
-        localstorage.getItem("jio/localstorage/upost/apost/post1"),
-        {
-            "_id": "post1",
-            "title": "myPost1"
-        },
-        "Check document"
-    );
-
-    // post but document already exists
-    o.spy (o, "status", 409, "Post but document already exists");
-    o.jio.post({"_id": "post1", "title": "myPost2"}, o.f);
-    o.tick(o);
-
-    o.jio.stop();
-});
-
-
-test ("Put", function(){
-
-    var o = generateTools(this);
-
-    o.jio = JIO.newJio({
-        "type": "local",
-        "username": "uput",
-        "application_name": "aput"
-    });
-
-    // put without id
-    // error 20 -> document id required
-    o.spy (o, "status", 20, "Put without id");
-    o.jio.put({}, o.f);
-    o.tick(o);
-
-    // put non empty document
-    o.spy (o, "value", {"ok": true, "id": "put1"}, "Creates a document");
-    o.jio.put({"_id": "put1", "title": "myPut1"}, o.f);
-    o.tick(o);
-
-    // check document
-    deepEqual(
-        localstorage.getItem("jio/localstorage/uput/aput/put1"),
-        {
-            "_id": "put1",
-            "title": "myPut1"
-        },
-        "Check document"
-    );
-
-    // put but document already exists
-    o.spy (o, "value", {"ok": true, "id": "put1"}, "Update the document");
-    o.jio.put({"_id": "put1", "title": "myPut2"}, o.f);
-    o.tick(o);
-
-    // check document
-    deepEqual(
-        localstorage.getItem("jio/localstorage/uput/aput/put1"),
-        {
-            "_id": "put1",
-            "title": "myPut2"
-        },
-        "Check document"
-    );
-
-    o.jio.stop();
-
-});
-
-test ("PutAttachment", function(){
-
-    var o = generateTools(this);
-
-    o.jio = JIO.newJio({
-        "type": "local",
-        "username": "uputattmt",
-        "application_name": "aputattmt"
-    });
-
-    // putAttachment without doc id
-    // error 20 -> document id required
-    o.spy(o, "status", 20, "PutAttachment without doc id");
-    o.jio.putAttachment({}, o.f);
-    o.tick(o);
-
-    // putAttachment without attachment id
-    // error 22 -> attachment id required
-    o.spy(o, "status", 22, "PutAttachment without attachment id");
-    o.jio.putAttachment({"_id": "putattmt1"}, o.f);
-    o.tick(o);
-
-    // putAttachment without document
-    // error 404 -> not found
-    o.spy(o, "status", 404, "PutAttachment without document");
-    o.jio.putAttachment({"_id": "putattmt1", "_attachment": "putattmt2"}, o.f);
-    o.tick(o);
-
-    // adding a document
-    localstorage.setItem("jio/localstorage/uputattmt/aputattmt/putattmt1", {
-        "_id": "putattmt1",
-        "title": "myPutAttmt1"
-    });
-
-    // putAttachment with document
-    o.spy(o, "value",
-          {"ok": true, "id": "putattmt1", "attachment": "putattmt2"},
-          "PutAttachment with document, without data");
-    o.jio.putAttachment({"_id": "putattmt1", "_attachment": "putattmt2"}, o.f);
-    o.tick(o);
-
-    // check document
-    deepEqual(
-        localstorage.getItem("jio/localstorage/uputattmt/aputattmt/putattmt1"),
-        {
-            "_id": "putattmt1",
-            "title": "myPutAttmt1",
-            "_attachments": {
-                "putattmt2": {
-                    "length": 0,
-                    // md5("")
-                    "digest": "md5-d41d8cd98f00b204e9800998ecf8427e"
-                }
-            }
-        },
-        "Check document"
-    );
-
-    // check attachment
-    deepEqual(
-        localstorage.getItem(
-            "jio/localstorage/uputattmt/aputattmt/putattmt1/putattmt2"),
-        "", "Check attachment"
-    );
-
-    // update attachment
-    o.spy(o, "value",
-          {"ok": true, "id": "putattmt1", "attachment": "putattmt2"},
-          "Update Attachment, with data");
-    o.jio.putAttachment({
-      "_id": "putattmt1",
-      "_attachment": "putattmt2",
-      "_data": "abc"
-    }, o.f);
-    o.tick(o);
-
-    // check document
-    deepEqual(
-        localstorage.getItem("jio/localstorage/uputattmt/aputattmt/putattmt1"),
-        {
-            "_id": "putattmt1",
-            "title": "myPutAttmt1",
-            "_attachments": {
-                "putattmt2": {
-                    "length": 3,
-                    // md5("abc")
-                    "digest": "md5-900150983cd24fb0d6963f7d28e17f72"
-                }
-            }
-        },
-        "Check document"
-    );
-
-    // check attachment
-    deepEqual(
-        localstorage.getItem(
-            "jio/localstorage/uputattmt/aputattmt/putattmt1/putattmt2"),
-        "abc", "Check attachment"
-    );
-
-    o.jio.stop();
-});
-
-test ("Get", function(){
-
-    var o = generateTools(this);
-
-    o.jio = JIO.newJio({
-        "type": "local",
-        "username": "uget",
-        "application_name": "aget"
-    });
-
-    // get inexistent document
-    o.spy(o, "status", 404, "Get inexistent document");
-    o.jio.get({"_id": "get1"}, o.f);
-    o.tick(o);
-
-    // get inexistent attachment
-    o.spy(o, "status", 404, "Get inexistent attachment");
-    o.jio.getAttachment({"_id": "get1", "_attachment": "get2"}, o.f);
-    o.tick(o);
-
-    // adding a document
-    o.doc_get1 = {
-        "_id": "get1",
-        "title": "myGet1"
-    };
-    localstorage.setItem("jio/localstorage/uget/aget/get1", o.doc_get1);
-
-    // get document
-    o.spy(o, "value", o.doc_get1, "Get document");
-    o.jio.get({"_id": "get1"}, o.f);
-    o.tick(o);
-
-    // get inexistent attachment (document exists)
-    o.spy(o, "status", 404, "Get inexistent attachment (document exists)");
-    o.jio.getAttachment({"_id": "get1", "_attachment": "get2"}, o.f);
-    o.tick(o);
-
-    // adding an attachment
-    o.doc_get1["_attachments"] = {
-        "get2": {
-            "length": 2,
-            // md5("de")
-            "digest": "md5-5f02f0889301fd7be1ac972c11bf3e7d"
-        }
-    };
-    localstorage.setItem("jio/localstorage/uget/aget/get1", o.doc_get1);
-    localstorage.setItem("jio/localstorage/uget/aget/get1/get2", "de");
-
-    // get attachment
-    o.spy(o, "value", "de", "Get attachment");
-    o.jio.getAttachment({"_id": "get1", "_attachment": "get2"}, o.f);
-    o.tick(o);
-
-    o.jio.stop();
-
-});
-
-test ("Remove", function(){
-
-    var o = generateTools(this);
-
-    o.jio = JIO.newJio({
-        "type": "local",
-        "username": "uremove",
-        "application_name": "aremove"
-    });
-
-    // remove inexistent document
-    o.spy(o, "status", 404, "Remove inexistent document");
-    o.jio.remove({"_id": "remove1"}, o.f);
-    o.tick(o);
-
-    // remove inexistent attachment
-    o.spy(o, "status", 404, "Remove inexistent attachment");
-    o.jio.removeAttachment({"_id": "remove1", "_attachment": "remove2"}, o.f);
-    o.tick(o);
-
-    // adding a document + attmt
-    localstorage.setItem("jio/localstorage/uremove/aremove/remove1", {
-        "_id": "remove1",
-        "title": "myRemove1",
-        "_attachments": {
-            "remove2": {
-                "length": 4,
-                "digest": "md5-blahblah"
-            }
-        }
-    });
-    localstorage.setItem(
-        "jio/localstorage/uremove/aremove/remove1/remove2", "fghi");
-
-    // remove attachment
-    o.spy(o, "value", {"ok": true, "id": "remove1", "attachment": "remove2"},
-          "Remove document");
-    o.jio.removeAttachment({"_id": "remove1", "_attachment": "remove2"}, o.f);
-    o.tick(o);
-
-    // remove document
-    o.spy(o, "value", {"ok": true, "id": "remove1"}, "Remove document");
-    o.jio.remove({"_id": "remove1"}, o.f);
-    o.tick(o);
-
-    // check document
-    ok(localstorage.getItem("jio/localstorage/uremove/aremove/remove1")===null,
-       "Check document is removed");
-
-    // adding a document + attmt
-    localstorage.setItem("jio/localstorage/uremove/aremove/remove1", {
-        "_id": "remove1",
-        "title": "myRemove1",
-        "_attachments": {
-            "remove2": {
-                "length": 4,
-                "digest": "md5-blahblah"
-            }
-        }
-    });
-    localstorage.setItem(
-        "jio/localstorage/uremove/aremove/remove1/remove2", "fghi");
-
-    // remove attachment
-    o.spy(o, "value", {"ok": true, "id": "remove1"},
-          "Remove document and attachment");
-    o.jio.remove({"_id": "remove1"}, o.f);
-    o.tick(o);
-    ok(localstorage.getItem("jio/localstorage/uremove/aremove/remove1"
-       )===null, "Check document is removed");
-    ok(localstorage.getItem("jio/localstorage/uremove/aremove/remove1/remove2"
-      )===null, "Check attachment is removed");
-
-    o.jio.stop();
-
-});
-
-
-test ("AllDocs", function(){
-
-    var o = generateTools(this), i, m = 15;
-
-    o.jio = JIO.newJio({
-        "type": "local",
-        "username": "ualldocs",
-        "application_name": "aalldocs"
-    });
-    o.localpath = "jio/localstorage/ualldocs/aalldocs";
-
-    // sample data
-    o.titles = ["Shawshank Redemption", "Godfather", "Godfather 2",
-      "Pulp Fiction", "The Good, The Bad and The Ugly", "12 Angry Men",
-      "The Dark Knight", "Schindlers List",
-      "Lord of the Rings - Return of the King", "Fight Club",
-      "Star Wars Episode V", "Lord Of the Rings - Fellowship of the Ring",
-      "One flew over the Cuckoo's Nest", "Inception", "Godfellas"
-    ];
-    o.years = [1994,1972,1974,1994,1966,1957,2008,1993,2003,1999,1980,2001,
-      1975,2010,1990
-    ];
-    o.director = ["Frank Darabont", "Francis Ford Coppola",
-      "Francis Ford Coppola", "Quentin Tarantino", "Sergio Leone",
-      "Sidney Lumet", "Christopher Nolan", "Steven Spielberg",
-      "Peter Jackson", "David Fincher", "Irvin Kershner", "Peter Jackson",
-      "Milos Forman", "Christopher Nolan", " Martin Scorsese"
-    ]
-
-    for (i = 0; i < m; i += 1) {
-      o.fakeDoc = {};
-      o.fakeDoc._id = "doc_"+(i < 10 ? "0"+i : i);
-      o.fakeDoc.title = o.titles[i];
-      o.fakeDoc.year = o.years[i];
-      o.fakeDoc.author = o.director[i];
-      if (i === 5) {
-        o.fakeDoc._attachments = {
-          "att": {
-            "digest": "md5-dontcare",
-            "content_type": "text/plain",
-            "length": 3
-          }
-        };
-        localstorage.setItem(o.localpath + "/doc_05/att", "abc");
-      }
-      localstorage.setItem(o.localpath+"/doc_"+(i < 10 ? "0"+i : i), o.fakeDoc);
-    }
-
-    // response
-    o.allDocsResponse = {};
-    o.allDocsResponse.rows = [];
-    o.allDocsResponse.total_rows = 15;
-    for (i = 0; i < m; i += 1) {
-      o.allDocsResponse.rows.push({
-        "id": "doc_"+(i < 10 ? "0"+i : i),
-        "key": "doc_"+(i < 10 ? "0"+i : i),
-        "value": {}
-      });
-    };
-    // alldocs
-    o.spy(o, "value", o.allDocsResponse, "All docs");
-    o.jio.allDocs(function (err, response) {
-      if (response && response.rows) {
-        response.rows.sort(function (a, b) {
-          return a.id > b.id ? 1 : a.id < b.id ? -1 : 0;
-        });
-      }
-      o.f(err, response);
-    });
-    o.tick(o);
-
-    // include docs
-    o.allDocsResponse = {};
-    o.allDocsResponse.rows = [];
-    o.allDocsResponse.total_rows = m;
-    for (i = 0; i < m; i += 1) {
-      o.allDocsResponse.rows.push({
-        "id": "doc_"+(i < 10 ? "0"+i : i),
-        "key": "doc_"+(i < 10 ? "0"+i : i),
-        "value": {},
-        "doc": localstorage.getItem(o.localpath+"/doc_"+(i < 10 ? "0"+i : i))
-      });
-    };
-
-    // alldocs
-    o.spy(o, "value", o.allDocsResponse, "All docs (include docs)");
-    o.jio.allDocs({"include_docs": true}, function (err, response) {
-      if (response && response.rows) {
-        response.rows.sort(function (a, b) {
-          return a.id > b.id ? 1 : a.id < b.id ? -1 : 0;
-        });
-      }
-      o.f(err, response);
-    });
-    o.tick(o);
-
-    // complex queries
-    o.thisShouldBeTheAnswer4 = {"total_rows": 0, "rows": []};
-    o.allDocsResponse.rows.forEach(function (row) {
-      var new_row;
-      if (row.doc.year >= 1980) {
-        new_row = JSON.parse(JSON.stringify(row));
-        new_row.value.title = row.doc.title;
-        new_row.value.year = row.doc.year;
-        delete new_row.doc;
-        o.thisShouldBeTheAnswer4.rows.push(new_row);
-        o.thisShouldBeTheAnswer4.total_rows += 1;
-      }
-    });
-    o.thisShouldBeTheAnswer4.rows.sort(function (a, b) {
-      return a.value.year > b.value.year ? -1 :
-        a.value.year < b.value.year ? 1 : 0;
-    });
-    o.thisShouldBeTheAnswer4.total_rows = 5;
-    o.thisShouldBeTheAnswer4.rows.length = 5;
-
-    o.spy(o, "value", o.thisShouldBeTheAnswer4,
-      "allDocs (complex queries year >= 1980, all query options)");
-    o.jio.allDocs({
-      "query": '(year: >= "1980")',
-      "limit": [0,5],
-      "sort_on": [["year", "descending"]],
-      "select_list": ["title", "year"]
-    }, o.f);
-    o.tick(o);
-
-    // empty query returns all
-    o.thisShouldBeTheAnswer5 = {"total_rows": 0, "rows": []};
-    o.allDocsResponse.rows.forEach(function (row) {
-      var new_row = JSON.parse(JSON.stringify(row));
-      new_row.value.title = row.doc.title;
-      o.thisShouldBeTheAnswer5.rows.push(new_row);
-      o.thisShouldBeTheAnswer5.total_rows += 1;
-    });
-    o.thisShouldBeTheAnswer5.rows.sort(function (a, b) {
-      return a.value.title > b.value.title ? -1 :
-        a.value.title < b.value.title ? 1 : 0;
-    });
-
-    o.spy(o, "value", o.thisShouldBeTheAnswer5,
-      "allDocs (empty query in complex query)");
-
-    o.jio.allDocs({
-      "sort_on": [["title", "descending"]],
-      "select_list": ["title"],
-      "include_docs": true
-    }, o.f);
-    o.tick(o);
-
-    o.jio.stop();
-
-});
-
 module ( "Jio Revision Storage + Local Storage" );
 
 test ("Post", function(){
-- 
GitLab