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,347 +53,159 @@
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"]]
sort_on: [["timestamp", "ascending"]]
});
})
.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);
})
.push(function (result) {
deepEqual(result, {
title: "foo0",
_doc_id: "bar",
_timestamp: result._timestamp,
_deprecated: "true"
},
"Query returns the first edition of the document");
})
.fail(function (error) {
//console.log(error);
ok(false, error);
9,
"All nine versions exist in storage");
return not_bryan.get(results.data.rows[0].id);
})
.always(function () {start(); });
});
/////////////////////////////////////////////////////////////////
// bryanStorage revision history
/////////////////////////////////////////////////////////////////
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']]
.push(function (results) {
deepEqual(results, {
doc_id: "bar",
doc: {
title: "foo0"
},
query_input2 =
{
query: 'title: "rev1"',
sort_on: [['_timestamp', 'descending']]
};
jio.put("doc1", {
"title": "rev0",
"subtitle": "subrev0"
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.put("doc1", {
"title": "rev1",
"subtitle": "subrev1"
});
return jio.get("bar");
})
.push(function () {
return jio.put("doc1", {
"title": "rev2",
"subtitle": "subrev2"
});
})
.push(function () {
return jio.put("doc1", {
"title": "rev3",
"subtitle": "subrev3"
});
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 () {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: ''});
ok(result.title !== undefined, "barbar exists and has proper form");
return not_bryan.allDocs({
query: "",
sort_on: [["op", "descending"]]
});
})
.push(function (results) {
equal(results.data.rows.length,
1,
"bryanstorage only sees latest version");
return jio.get(results.data.rows[0].id);
10,
"Remove operation is recorded");
return not_bryan.get(results.data.rows[0].id);
})
.push(function (result) {
deepEqual(result, {
"title": "rev3",
"subtitle": "subrev3"
}, "Retrieve latest version correctly with bryanstorage");
doc_id: "bar",
timestamp: result.timestamp,
op: "remove"
});
})
.fail(function (error) {
//console.log(error);
ok(false, error);
})
.always(function () {
start();
});
.always(function () {start(); });
});
/////////////////////////////////////////////////////////////////
// bryanStorage.revision_history_multiple_edits
// bryanStorage.querying_from_bryanstorage
/////////////////////////////////////////////////////////////////
module("bryanStorage.revision_history_multiple_edits");
test("modify first version but save both", function () {
module("bryanStorage.querying_from_bryanstorage");
test("verifying the correct results are returned from bryanStorage.allDocs",
function () {
stop();
expect(10);
var dbname = "rev_hist_mult_db" + Date.now(),
jio = jIO.createJIO({
expect(1);
// 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
type: "memory"
}
}
}),
not_bryan = jIO.createJIO({
type: "query",
sub_storage: {
type: "uuid",
sub_storage: {
type: "indexeddb",
database: dbname
}
}
});
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"
});
})
jio.put("bar", {"title": "foo0"})
.push(function () {
return jio.put("main_doc", {
"title": "rev1",
"subtitle": "subrev1"
});
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("main_doc", {
"title": "rev2",
"subtitle": "subrev2"
});
return jio.put("bar", {"title": "foo4"});
})
.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");
return jio.put("barbar", {"title": "attr3"});
})
// Queries should only include information about the final two versions
.push(function () {
return jio.allDocs({
query: ""
query: "",
sort_on: [["title", "ascending"]]
});
})
.push(function (result) {
equal(result.data.rows.length,
.push(function (results) {
equal(results.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);
}
"Empty query yields two results since there are two unique docs");
return jio.get(results.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");
title: "attr3"
}, "Retrieve the first title in the correct format (no metadata)");
},
function () {
return ok(false, "Couldn't find document in storage");
})
// Querying for a specific id
.push(function () {
return jio.allDocs({
query: ''
query: "id: bar"
});
})
.push(function (result) {
equal(result.data.rows.length, 2, "Correct number of results returned");
})
// 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"
},
"Get the earliest copy of the doc with all metadata.");
deepEqual(result, {
title: "foo4"
}, "Retrieve correct document in correct format (no metadata)");
})
// 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();
});
.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