[Persistence] Move persistence sources

Move persistence sources into a deeper bundle in preparation for
splitting of persistence into two bundles - one specific to the
Couch adapter, and one for persistence caching generally. This
supports WTD-702, the WARP persistence adapter, which will not
use Couch for persistence but which will want to use the platform-
provided persistence cache.
This commit is contained in:
Victor Woeltjen
2015-01-20 12:12:17 -08:00
parent af3175ca07
commit 9e981d2579
11 changed files with 0 additions and 0 deletions

View File

@@ -0,0 +1,82 @@
/*global define,Promise,describe,it,expect,beforeEach,waitsFor,jasmine*/
define(
["../src/CachingPersistenceDecorator"],
function (CachingPersistenceDecorator) {
"use strict";
var PERSISTENCE_METHODS = [
"listSpaces",
"listObjects",
"createObject",
"readObject",
"updateObject",
"deleteObject"
];
describe("The caching persistence decorator", function () {
var testSpace,
mockPersistence,
mockCallback,
decorator;
function mockPromise(value) {
return {
then: function (callback) {
return mockPromise(callback(value));
}
};
}
beforeEach(function () {
testSpace = "TEST";
mockPersistence = jasmine.createSpyObj(
"persistenceService",
PERSISTENCE_METHODS
);
mockCallback = jasmine.createSpy("callback");
PERSISTENCE_METHODS.forEach(function (m) {
mockPersistence[m].andReturn(mockPromise({
method: m
}));
});
decorator = new CachingPersistenceDecorator(
testSpace,
mockPersistence
);
});
it("delegates all methods", function () {
PERSISTENCE_METHODS.forEach(function (m) {
// Reset the callback
mockCallback = jasmine.createSpy("callback");
// Invoke the method; avoid using a key that will be cached
decorator[m](testSpace, "testKey" + m, "testValue")
.then(mockCallback);
// Should have gotten that method's plain response
expect(mockCallback).toHaveBeenCalledWith({ method: m });
});
});
it("does not repeat reads of cached objects", function () {
// Perform two reads
decorator.readObject(testSpace, "someKey", "someValue")
.then(mockCallback);
decorator.readObject(testSpace, "someKey", "someValue")
.then(mockCallback);
// Should have only delegated once
expect(mockPersistence.readObject.calls.length).toEqual(1);
// But both promises should have resolved
expect(mockCallback.calls.length).toEqual(2);
});
});
}
);

View File

@@ -0,0 +1,44 @@
/*global define,Promise,describe,it,expect,beforeEach,waitsFor,jasmine*/
/**
* DomainObjectProviderSpec. Created by vwoeltje on 11/6/14.
*/
define(
["../src/CouchDocument"],
function (CouchDocument) {
"use strict";
// JSLint doesn't like dangling _'s, but CouchDB uses these, so
// hide this behind variables.
var REV = "_rev",
ID = "_id",
DELETED = "_deleted";
describe("A couch document", function () {
it("includes an id", function () {
expect(new CouchDocument("testId", {})[ID])
.toEqual("testId");
});
it("includes a rev only when one is provided", function () {
expect(new CouchDocument("testId", {})[REV])
.not.toBeDefined();
expect(new CouchDocument("testId", {}, "testRev")[REV])
.toEqual("testRev");
});
it("includes the provided model", function () {
var model = { someKey: "some value" };
expect(new CouchDocument("testId", model).model)
.toEqual(model);
});
it("marks documents as deleted only on request", function () {
expect(new CouchDocument("testId", {}, "testRev")[DELETED])
.not.toBeDefined();
expect(new CouchDocument("testId", {}, "testRev", true)[DELETED])
.toBe(true);
});
});
}
);

View File

@@ -0,0 +1,111 @@
/*global define,Promise,describe,it,expect,beforeEach,waitsFor,jasmine*/
define(
["../src/CouchIndicator"],
function (CouchIndicator) {
"use strict";
describe("The CouchDB status indicator", function () {
var mockHttp,
mockInterval,
testPath,
testInterval,
mockPromise,
indicator;
beforeEach(function () {
mockHttp = jasmine.createSpyObj("$http", [ "get" ]);
mockInterval = jasmine.createSpy("$interval");
mockPromise = jasmine.createSpyObj("promise", [ "then" ]);
testPath = "/test/path";
testInterval = 12321; // Some number
mockHttp.get.andReturn(mockPromise);
indicator = new CouchIndicator(
mockHttp,
mockInterval,
testPath,
testInterval
);
});
it("polls for changes", function () {
expect(mockInterval).toHaveBeenCalledWith(
jasmine.any(Function),
testInterval
);
});
it("has a database icon", function () {
expect(indicator.getGlyph()).toEqual("D");
});
it("consults the database at the configured path", function () {
expect(mockHttp.get).toHaveBeenCalledWith(testPath);
});
it("changes when the database connection is nominal", function () {
var initialText = indicator.getText(),
initialDescrption = indicator.getDescription(),
initialGlyphClass = indicator.getGlyphClass();
// Nominal just means getting back an objeect, without
// an error field.
mockPromise.then.mostRecentCall.args[0]({ data: {} });
// Verify that these values changed;
// don't test for specific text.
expect(indicator.getText()).not.toEqual(initialText);
expect(indicator.getGlyphClass()).not.toEqual(initialGlyphClass);
expect(indicator.getDescription()).not.toEqual(initialDescrption);
// Do check for specific class
expect(indicator.getGlyphClass()).toEqual("ok");
});
it("changes when the server reports an error", function () {
var initialText = indicator.getText(),
initialDescrption = indicator.getDescription(),
initialGlyphClass = indicator.getGlyphClass();
// Nominal just means getting back an objeect, with
// an error field.
mockPromise.then.mostRecentCall.args[0](
{ data: { error: "Uh oh." } }
);
// Verify that these values changed;
// don't test for specific text.
expect(indicator.getText()).not.toEqual(initialText);
expect(indicator.getGlyphClass()).not.toEqual(initialGlyphClass);
expect(indicator.getDescription()).not.toEqual(initialDescrption);
// Do check for specific class
expect(indicator.getGlyphClass()).toEqual("caution");
});
it("changes when the server cannot be reached", function () {
var initialText = indicator.getText(),
initialDescrption = indicator.getDescription(),
initialGlyphClass = indicator.getGlyphClass();
// Nominal just means getting back an objeect, without
// an error field.
mockPromise.then.mostRecentCall.args[1]({ data: {} });
// Verify that these values changed;
// don't test for specific text.
expect(indicator.getText()).not.toEqual(initialText);
expect(indicator.getGlyphClass()).not.toEqual(initialGlyphClass);
expect(indicator.getDescription()).not.toEqual(initialDescrption);
// Do check for specific class
expect(indicator.getGlyphClass()).toEqual("err");
});
});
}
);

View File

@@ -0,0 +1,171 @@
/*global define,Promise,describe,it,expect,beforeEach,waitsFor,jasmine*/
/**
* DomainObjectProviderSpec. Created by vwoeltje on 11/6/14.
*/
define(
["../src/CouchPersistenceProvider"],
function (CouchPersistenceProvider) {
"use strict";
describe("The couch persistence provider", function () {
var mockHttp,
mockQ,
testSpace = "testSpace",
testPath = "/test/db",
capture,
provider;
function mockPromise(value) {
return {
then: function (callback) {
return mockPromise(callback(value));
}
};
}
beforeEach(function () {
mockHttp = jasmine.createSpy("$http");
mockQ = jasmine.createSpyObj("$q", ["when"]);
mockQ.when.andCallFake(mockPromise);
// Capture promise results
capture = jasmine.createSpy("capture");
provider = new CouchPersistenceProvider(
mockHttp,
mockQ,
testSpace,
testPath
);
});
it("reports available spaces", function () {
provider.listSpaces().then(capture);
expect(capture).toHaveBeenCalledWith([testSpace]);
});
// General pattern of tests below is to simulate CouchDB's
// response, verify that request looks like what CouchDB
// would expect, and finally verify that CouchPersistenceProvider's
// return values match what is expected.
it("lists all available documents", function () {
mockHttp.andReturn(mockPromise({
data: { rows: [ { id: "a" }, { id: "b" }, { id: "c" } ] }
}));
provider.listObjects().then(capture);
expect(mockHttp).toHaveBeenCalledWith({
url: "/test/db/_all_docs", // couch document listing
method: "GET"
});
expect(capture).toHaveBeenCalledWith(["a", "b", "c"]);
});
it("allows object creation", function () {
var model = { someKey: "some value" };
mockHttp.andReturn(mockPromise({
data: { "_id": "abc", "_rev": "xyz", "ok": true }
}));
provider.createObject("testSpace", "abc", model).then(capture);
expect(mockHttp).toHaveBeenCalledWith({
url: "/test/db/abc",
method: "PUT",
data: {
"_id": "abc",
metadata: jasmine.any(Object),
model: model
}
});
expect(capture).toHaveBeenCalledWith(true);
});
it("allows object models to be read back", function () {
var model = { someKey: "some value" };
mockHttp.andReturn(mockPromise({
data: { "_id": "abc", "_rev": "xyz", "model": model }
}));
provider.readObject("testSpace", "abc").then(capture);
expect(mockHttp).toHaveBeenCalledWith({
url: "/test/db/abc",
method: "GET"
});
expect(capture).toHaveBeenCalledWith(model);
});
it("allows object update", function () {
var model = { someKey: "some value" };
// First do a read to populate rev tags...
mockHttp.andReturn(mockPromise({
data: { "_id": "abc", "_rev": "xyz", "model": {} }
}));
provider.readObject("testSpace", "abc");
// Now perform an update
mockHttp.andReturn(mockPromise({
data: { "_id": "abc", "_rev": "uvw", "ok": true }
}));
provider.updateObject("testSpace", "abc", model).then(capture);
expect(mockHttp).toHaveBeenCalledWith({
url: "/test/db/abc",
method: "PUT",
data: {
"_id": "abc",
"_rev": "xyz",
metadata: jasmine.any(Object),
model: model
}
});
expect(capture).toHaveBeenCalledWith(true);
});
it("allows object deletion", function () {
// First do a read to populate rev tags...
mockHttp.andReturn(mockPromise({
data: { "_id": "abc", "_rev": "xyz", "model": {} }
}));
provider.readObject("testSpace", "abc");
// Now perform an update
mockHttp.andReturn(mockPromise({
data: { "_id": "abc", "_rev": "uvw", "ok": true }
}));
provider.deleteObject("testSpace", "abc", {}).then(capture);
expect(mockHttp).toHaveBeenCalledWith({
url: "/test/db/abc",
method: "PUT",
data: {
"_id": "abc",
"_rev": "xyz",
"_deleted": true,
metadata: jasmine.any(Object),
model: {}
}
});
expect(capture).toHaveBeenCalledWith(true);
});
it("reports failure to create objects", function () {
var model = { someKey: "some value" };
mockHttp.andReturn(mockPromise({
data: { "_id": "abc", "_rev": "xyz", "ok": false }
}));
provider.createObject("testSpace", "abc", model).then(capture);
expect(capture).toHaveBeenCalledWith(false);
});
it("returns undefined when objects are not found", function () {
// Act like a 404
mockHttp.andReturn({
then: function (success, fail) {
return mockPromise(fail());
}
});
provider.readObject("testSpace", "abc").then(capture);
expect(capture).toHaveBeenCalledWith(undefined);
});
});
}
);

View File

@@ -0,0 +1,6 @@
[
"CachingPersistenceDecorator",
"CouchDocument",
"CouchIndicator",
"CouchPersistenceProvider"
]