Commit 0d5eba32 authored by Bryan Kaperick's avatar Bryan Kaperick

Revised bryanstorage so that a log of puts and removes is recorded. All tests...

Revised bryanstorage so that a log of puts and removes is recorded.  All tests involving getting, putting, removing, and querying from other storages are passing.  Currently, the allDocs method is not implemented correctly, but there are several placeholder tests written which demonstrate the desired functionality.
parent dc356c98
......@@ -3,8 +3,13 @@
(function (jIO) {
"use strict";
// Metadata keys included for internal revisioning, but not shown to user
//var _revision_metadata = ["_revision", "_doc_id"];
var unique_timestamp = function () {
// Used to distinguish between operations done within the same millisecond
var uuid = ('0000' + Math.floor(Math.random() * 0x10000)
.toString(16)).slice(-4),
timestamp = Date.now().toString();
return timestamp + "-" + uuid;
};
/**
* The jIO BryanStorage extension
......@@ -21,34 +26,69 @@
}
BryanStorage.prototype.get = function (id_in) {
return this._sub_storage.get(id_in);
// Query to get the last edit made to this document
var substorage = this._sub_storage,
options = {
query: "doc_id: " + id_in,
sort_on: [["timestamp", "descending"]],
limit: [0, 1]
};
return substorage.allDocs(options)
.push(function (results) {
if (results.data.rows.length > 0) {
return substorage.get(results.data.rows[0].id);
}
throw new jIO.util.jIOError(
"bryanstorage: cannot find object '" + id_in + "'",
404
);
})
.push(function (result) {
// If last edit was a remove, throw a 'not found' error
if (result.op === "remove") {
throw new jIO.util.jIOError(
"bryanstorage: cannot find object '" + id_in + "' (removed)",
404
);
}
// If last edit was a put, return the document data
if (result.op === "put") {
return result.doc;
}
});
};
BryanStorage.prototype.post = function (metadata) {
return this._sub_storage.post(metadata);
};
BryanStorage.prototype.put = function (id, metadata) {
var storage = this;
return this._sub_storage.put(id, metadata)
.push(function () {
BryanStorage.prototype.put = function (id, data) {
var timestamp = unique_timestamp(),
metadata = {
// XXX: remove this attribute once query can sort_on id
timestamp: timestamp,
doc_id: id,
doc: data,
op: "put"
};
return this._sub_storage.put(timestamp, metadata);
};
// Also push a metadata document recording the posting time
metadata._deprecated = "true";
metadata._doc_id = id;
metadata._timestamp = Date.now();
return storage.post(metadata);
});
BryanStorage.prototype.remove = function (id) {
var timestamp = unique_timestamp(),
metadata = {
// XXX: remove this attribute once query can sort_on id
timestamp: timestamp,
doc_id: id,
op: "remove"
};
return this._sub_storage.put(timestamp, metadata);
};
BryanStorage.prototype.allAttachments = function () {
return this._sub_storage.allAttachments.apply(this._sub_storage, arguments);
};
BryanStorage.prototype.remove = function () {
return this._sub_storage.remove.apply(this._sub_storage, arguments);
};
BryanStorage.prototype.getAttachment = function () {
return this._sub_storage.getAttachment.apply(this._sub_storage, arguments);
};
......@@ -107,14 +147,6 @@
};
BryanStorage.prototype.buildQuery = function (options) {
if (options === undefined) {
options = {query: ""};
}
if (options.query !== "") {
options.query = "(" + options.query + ") AND ";
}
options.query = options.query + 'NOT (_deprecated: "true")';
return this._sub_storage.buildQuery(options);
};
......
......@@ -15,11 +15,12 @@
/////////////////////////////////////////////////////////////////
// _revision parameter updating with RSVP all
/////////////////////////////////////////////////////////////////
module("bryanStorage revision with RSVP all");
module("bryanStorage.revision_with_RSVP_all");
test("verifying updates correctly when puts are done in parallel",
function () {
stop();
expect(3);
expect(7);
// create storage of type "bryan" with memory as substorage
var dbname = "rsvp_db_" + Date.now(),
......@@ -52,35 +53,72 @@
jio.put("bar", {"title": "foo1"}),
jio.put("bar", {"title": "foo2"}),
jio.put("bar", {"title": "foo3"}),
jio.put("bar", {"title": "foo4"})
jio.put("bar", {"title": "foo4"}),
jio.put("barbar", {"title": "attr0"}),
jio.put("barbar", {"title": "attr1"}),
jio.put("barbar", {"title": "attr2"}),
jio.put("barbar", {"title": "attr3"})
]);
})
.push(function () {return jio.get("bar"); })
.push(function (result) {
deepEqual(result, {
"title": "foo4"
ok(result.title !== "foo0", "Title should have changed from foo0");
})
.push(function () {
return not_bryan.allDocs({
query: "",
sort_on: [["timestamp", "ascending"]]
});
})
.push(function (results) {
equal(results.data.rows.length,
9,
"All nine versions exist in storage");
return not_bryan.get(results.data.rows[0].id);
})
.push(function (results) {
deepEqual(results, {
doc_id: "bar",
doc: {
title: "foo0"
},
timestamp: results.timestamp,
op: "put"
}, "The first item in the log is pushing bar's title to 'foo0'");
return jio.remove("bar");
})
.push(function () {
return jio.get("bar");
})
.push(function () {
return jio.get("barbar");
}, function (error) {
deepEqual(
error.message,
"bryanstorage: cannot find object 'bar' (removed)",
"Appropriate error is sent explaining object has been removed"
);
return jio.get("barbar");
})
.push(function (result) {
ok(result.title !== undefined, "barbar exists and has proper form");
return not_bryan.allDocs({
query: "",
sort_on: [["_timestamp", "ascending"]]
sort_on: [["op", "descending"]]
});
})
.push(function (results) {
equal(results.data.rows.length,
6,
"Storage contains all 5 revisions plus the most recent one.");
return not_bryan.get(results.data.rows[1].id);
10,
"Remove operation is recorded");
return not_bryan.get(results.data.rows[0].id);
})
.push(function (result) {
deepEqual(result, {
title: "foo0",
_doc_id: "bar",
_timestamp: result._timestamp,
_deprecated: "true"
},
"Query returns the first edition of the document");
doc_id: "bar",
timestamp: result.timestamp,
op: "remove"
});
})
.fail(function (error) {
//console.log(error);
......@@ -91,308 +129,83 @@
/////////////////////////////////////////////////////////////////
// bryanStorage revision history
// bryanStorage.querying_from_bryanstorage
/////////////////////////////////////////////////////////////////
module("bryanStorage.revision_history");
test("put and get the correct version", function () {
stop();
expect(7);
var dbname = "rev_hist_db" + Date.now(),
jio = jIO.createJIO({
type: "bryan",
sub_storage: {
type: "uuid",
sub_storage: {
//type: "memory"
type: "indexeddb",
database: dbname
}
}
}),
not_bryan = jIO.createJIO({
type: "uuid",
sub_storage: {
type: "query",
sub_storage: {
//type: "memory"
type: "indexeddb",
database: dbname
}
}
}),
query_input =
{
query: 'NOT (_deprecated: "true")',
sort_on: [['_timestamp', 'descending']]
},
query_input2 =
{
query: 'title: "rev1"',
sort_on: [['_timestamp', 'descending']]
};
jio.put("doc1", {
"title": "rev0",
"subtitle": "subrev0"
})
.push(function () {
return jio.put("doc1", {
"title": "rev1",
"subtitle": "subrev1"
});
})
.push(function () {
return jio.put("doc1", {
"title": "rev2",
"subtitle": "subrev2"
});
})
.push(function () {
return jio.put("doc1", {
"title": "rev3",
"subtitle": "subrev3"
});
})
.push(function () {return jio.get("doc1"); })
.push(function (result) {
deepEqual(result, {
"title": "rev3",
"subtitle": "subrev3"
}, "Retrieve first edition of document correctly");
})
.push(function () {
return not_bryan.allDocs(query_input);
})
.push(function (results) {
equal(results.data.rows.length, 1, "Only 1 version isn't _deprecated");
return jio.get(results.data.rows[0].id);
})
.push(function (result) {
deepEqual(result, {
"title": "rev3",
"subtitle": "subrev3"
}, "Retrieve most recent edition by querying NOT _deprecated");
})
.push(function () {
return not_bryan.allDocs(query_input2);
})
.push(function (results) {
equal(results.data.rows.length, 1, "Only one version is titled 'rev1'");
return jio.get(results.data.rows[0].id);
})
.push(function (result) {
deepEqual(result, {
"title": "rev1",
"subtitle": "subrev1",
"_deprecated": "true",
"_timestamp": result._timestamp,
"_doc_id": "doc1"
},
"Retrieve 1st edit by querying for title: 'rev1' with other storage");
})
.push(function () {
return jio.allDocs({query: ''});
})
.push(function (results) {
equal(results.data.rows.length,
1,
"bryanstorage only sees latest version");
return jio.get(results.data.rows[0].id);
})
.push(function (result) {
deepEqual(result, {
"title": "rev3",
"subtitle": "subrev3"
}, "Retrieve latest version correctly with bryanstorage");
})
.fail(function (error) {
//console.log(error);
ok(false, error);
})
.always(function () {
start();
});
});
/////////////////////////////////////////////////////////////////
// bryanStorage.revision_history_multiple_edits
/////////////////////////////////////////////////////////////////
module("bryanStorage.querying_from_bryanstorage");
test("verifying the correct results are returned from bryanStorage.allDocs",
function () {
stop();
expect(1);
module("bryanStorage.revision_history_multiple_edits");
test("modify first version but save both", function () {
stop();
expect(10);
var dbname = "rev_hist_mult_db" + Date.now(),
jio = jIO.createJIO({
// create storage of type "bryan" with memory as substorage
var jio = jIO.createJIO({
type: "bryan",
sub_storage: {
type: "uuid",
sub_storage: {
type: "indexeddb",
database: dbname
}
}
}),
not_bryan = jIO.createJIO({
type: "query",
sub_storage: {
type: "uuid",
sub_storage: {
type: "indexeddb",
database: dbname
type: "memory"
}
}
});
jio.put("main_doc", {
"title": "rev0",
"subtitle": "subrev0"
})
.push(function () {
return jio.put("other_doc", {
"attr": "version0",
"subattr": "subversion0"
});
})
.push(function () {
return jio.put("other_doc", {
"attr": "version1",
"subattr": "subversion1"
});
})
.push(function () {
return jio.put("main_doc", {
"title": "rev1",
"subtitle": "subrev1"
});
})
.push(function () {
return jio.put("main_doc", {
"title": "rev2",
"subtitle": "subrev2"
});
})
.push(function () {
return jio.put("main_doc", {
"title": "rev3",
"subtitle": "subrev3"
});
})
.push(function () {return jio.get("main_doc"); })
.push(function (result) {
deepEqual(result, {
"title": "rev3",
"subtitle": "subrev3"
}, "Retrieve main document correctly");
})
.push(function () {return jio.get("other_doc"); })
.push(function (result) {
deepEqual(result, {
"attr": "version1",
"subattr": "subversion1"
}, "Retrieve other document correctly");
})
.push(function () {
return jio.allDocs({
query: ""
});
})
.push(function (result) {
equal(result.data.rows.length,
2,
"Empty query returns only non-deprecated docs");
})
.push(function () {
return jio.allDocs({
query: 'attr: "version1"'
});
})
.push(function (result) {
equal(result.data.rows.length,
1,
"No deprecated results are returned.");
if (result.data.rows.length > 0) {
return jio.get(result.data.rows[0].id);
}
})
.push(function (result) {
deepEqual(result, {
"attr": "version1",
"subattr": "subversion1"
}, "Only get most recent edit");
})
.push(function () {
return jio.allDocs({
query: '(_doc_id: "other_doc")'
});
})
.push(function (result) {
equal(result.data.rows.length, 0, "Correct number of results returned");
})
.push(function () {
return jio.allDocs({
query: ''
});
})
.push(function (result) {
equal(result.data.rows.length, 2, "Correct number of results returned");
})
jio.put("bar", {"title": "foo0"})
.push(function () {
return RSVP.all([
jio.put("bar", {"title": "foo1"}),
jio.put("bar", {"title": "foo2"}),
jio.put("bar", {"title": "foo3"}),
jio.put("barbar", {"title": "attr0"}),
jio.put("barbar", {"title": "attr1"}),
jio.put("barbar", {"title": "attr2"})
]);
})
// Make two final puts so we know what to expect as the current state of
// each document.
.push(function () {
return jio.put("bar", {"title": "foo4"});
})
.push(function () {
return jio.put("barbar", {"title": "attr3"});
})
// When not_bryan queries the storage, all documents are returned.
.push(function () {
var options = {
query: "_doc_id: main_doc",
sort_on: [["_timestamp", "ascending"]]
};
return not_bryan.allDocs(options);
})
.push(function (results) {
equal(results.data.rows.length,
4,
"should get all 3 deprecated versions plus one copy of the latest.");
return not_bryan.get(results.data.rows[0].id);
})
.push(function (results) {
deepEqual(results, {
"title": "rev0",
"subtitle": "subrev0",
"_doc_id": "main_doc",
"_timestamp": results._timestamp,
"_deprecated": "true"
// Queries should only include information about the final two versions
.push(function () {
return jio.allDocs({
query: "",
sort_on: [["title", "ascending"]]
});
})
.push(function (results) {
equal(results.data.rows.length,
2,
"Empty query yields two results since there are two unique docs");
return jio.get(results.data.rows[0].id);
})
.push(function (result) {
deepEqual(result, {
title: "attr3"
}, "Retrieve the first title in the correct format (no metadata)");
},
"Get the earliest copy of the doc with all metadata.");
})
function () {
return ok(false, "Couldn't find document in storage");
})
// When not_bryan queries the storage, all documents are returned.
.push(function () {
var options = {
query: "_doc_id: main_doc",
sort_on: [["_timestamp", "ascending"]]
};
return not_bryan.allDocs(options);
})
.push(function (results) {
return not_bryan.get(results.data.rows[1].id);
})
.push(function (results) {
deepEqual(results, {
"title": "rev1",
"subtitle": "subrev1",
"_doc_id": "main_doc",
"_timestamp": results._timestamp,
"_deprecated": "true"
},
"Get the earliest copy of the doc with all metadata.");
})
.fail(function (error) {
//console.log(error);
ok(false, error);
})
.always(function () {
start();
});
});
// Querying for a specific id
.push(function () {
return jio.allDocs({
query: "id: bar"
});
})
.push(function (result) {
deepEqual(result, {
title: "foo4"
}, "Retrieve correct document in correct format (no metadata)");
})
.fail(function (error) {
//console.log(error);
ok(false, error);
})
.always(function () {start(); });
});
}(jIO, QUnit));
\ No newline at end of file
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment