orchestrate
Version:
Orchestrate is a database service. It is a simple REST API that is optimized for queries. Orchestrate combines full-text search, graph, time-series, and key/value.
426 lines (395 loc) • 13.1 kB
JavaScript
// Copyright 2014 Orchestrate, Inc.
/**
* @fileoverview Test Patch methods.
*/
// Module Dependencies.
var assert = require('assert');
var db = require('./creds')();
var util = require('util');
var Q = require('kew');
var collection = 'patch.test_' + process.version;
var key = 'test-key-1-' + Math.floor(Math.random() * 10000);
// example doc used in all tests. it is reset every before each test.
var orig = {
"name":"test",
"age" : 99,
"tags" : ["coder"],
"info" : {"email":"test@test.com"}
};
suite('Patch', function () {
setup(function() {
return db.put(collection, key, orig)
.then(function (res){
assert.equal(201, res.statusCode);
return Q.resolve(res);
});
});
test('Add field', function () {
return db.newPatchBuilder(collection, key)
.add("email","test@test.com")
.apply()
.then(function (res) {
assert.equal(201, res.statusCode);
return db.get(collection, key);
})
.then(function (res) {
assert.equal(200, res.statusCode);
assert.equal("test@test.com", res.body.email);
return Q.resolve(res);
});
});
test('Remove field', function () {
return db.newPatchBuilder(collection, key)
.remove("name")
.apply()
.then(function (res) {
assert.equal(201, res.statusCode);
return db.get(collection, key);
})
.then(function (res) {
assert.equal(200, res.statusCode);
assert(typeof res.body.name === 'undefined');
return Q.resolve(res);
});
});
test('Remove field fails when not found', function () {
return db.newPatchBuilder(collection, key)
.remove("some_missing_field_name")
.apply()
.then(function (res) {
Q.reject("Should have failed with a 409.");
})
.fail(function (e) {
return Q.resolve(e);
});
});
test('Replace field', function () {
return db.newPatchBuilder(collection, key)
.replace("name", "name 2")
.apply()
.then(function (res) {
assert.equal(201, res.statusCode);
return db.get(collection, key);
})
.then(function (res) {
assert.equal(200, res.statusCode);
assert.equal('name 2', res.body.name);
return Q.resolve(res);
});
});
test('Move field', function () {
return db.newPatchBuilder(collection, key)
.move("name", "fullName")
.apply()
.then(function (res) {
assert.equal(201, res.statusCode);
return db.get(collection, key);
})
.then(function (res) {
assert.equal(200, res.statusCode);
assert(typeof res.body.name === 'undefined');
assert.equal('test', res.body.fullName);
return Q.resolve(res);
});
});
test('Copy field', function () {
return db.newPatchBuilder(collection, key)
.copy("name", "fullName")
.apply()
.then(function (res) {
assert.equal(201, res.statusCode);
return db.get(collection, key);
})
.then(function (res) {
assert.equal(200, res.statusCode);
assert.equal('test', res.body.name);
assert.equal('test', res.body.fullName);
return Q.resolve(res);
});
});
test('Increment field', function () {
return db.newPatchBuilder(collection, key)
.inc("age")
.apply()
.then(function (res) {
assert.equal(201, res.statusCode);
return db.get(collection, key);
})
.then(function (res) {
assert.equal(100, res.body.age);
return Q.resolve(res);
});
});
test('Increment field by value', function () {
return db.newPatchBuilder(collection, key)
.inc("age", 100)
.apply()
.then(function (res) {
assert.equal(201, res.statusCode);
return db.get(collection, key);
})
.then(function (res) {
assert.equal(199, res.body.age);
return Q.resolve(res);
});
});
test('Test field', function () {
return db.newPatchBuilder(collection, key)
.test("name", "test")
.inc("age")
.apply()
.then(function (res) {
assert.equal(201, res.statusCode);
return db.get(collection, key);
})
.then(function (res) {
assert.equal(200, res.statusCode);
assert.equal(100, res.body.age);
return Q.resolve(res);
});
});
test('Patch not applied when Test op fails', function () {
return db.newPatchBuilder(collection, key)
.test("name", "foo")
.inc("age")
.apply()
.then(function (res) {
return Q.reject("Should have failed with 409");
})
.fail(function (e) {
assert.equal(409, e.statusCode);
return db.get(collection, key)
.then(function(res2) {
assert.equal(200, res2.statusCode);
assert.equal(99, res2.body.age);
return Q.resolve(res2);
});
});
});
test('Test field with negation', function () {
return db.newPatchBuilder(collection, key)
.testNot("name", "foo")
.inc("age")
.apply()
.then(function (res) {
assert.equal(201, res.statusCode);
return db.get(collection, key);
})
.then(function (res) {
assert.equal(200, res.statusCode);
assert.equal(100, res.body.age);
return Q.resolve(res);
});
});
test('Init field', function () {
return db.newPatchBuilder(collection, key)
.init("profile", {"email":"foo@foo.com"})
.add("profile.description", "test")
.apply()
.then(function (res) {
assert.equal(201, res.statusCode);
return db.get(collection, key);
})
.then(function (res) {
assert.equal(200, res.statusCode);
assert.equal('foo@foo.com', res.body.profile.email);
assert.equal('test', res.body.profile.description);
return Q.resolve(res);
});
});
test('Init field does nothing when field already present', function () {
return db.newPatchBuilder(collection, key)
.init("info", {"email":"foo@foo.com"})
.add("info.description", "test")
.apply()
.then(function (res) {
assert.equal(201, res.statusCode);
return db.get(collection, key);
})
.then(function (res) {
assert.equal(200, res.statusCode);
// email should be the original, since init op will do nothing when
// the field is already present.
assert.equal('test@test.com', res.body.info.email);
assert.equal('test', res.body.info.description);
return Q.resolve(res);
});
});
test('Append value to array', function () {
return db.newPatchBuilder(collection, key)
.append("tags", "deranged")
.apply()
.then(function (res) {
assert.equal(201, res.statusCode);
return db.get(collection, key);
})
.then(function (res) {
assert.equal(200, res.statusCode);
assert.deepEqual(['coder', 'deranged'], res.body.tags);
return Q.resolve(res);
});
});
test('Append multiple values to array', function () {
return db.newPatchBuilder(collection, key)
.append("tags", ["deranged","coffee"])
.apply()
.then(function (res) {
assert.equal(201, res.statusCode);
return db.get(collection, key);
})
.then(function (res) {
assert.equal(200, res.statusCode);
assert.deepEqual(['coder', 'deranged', 'coffee'], res.body.tags);
return Q.resolve(res);
});
});
test('Merge object into field value', function () {
return db.newPatchBuilder(collection, key)
.merge("info", {"description":"coffee"})
.apply()
.then(function (res) {
assert.equal(201, res.statusCode);
return db.get(collection, key);
})
.then(function (res) {
assert.equal(200, res.statusCode);
assert.equal('test@test.com', res.body.info.email);
assert.equal('coffee', res.body.info.description);
return Q.resolve(res);
});
});
test('Nested Patch applied to field value', function () {
return db.newPatchBuilder(collection, key)
.patch("info", [{"op":"add","path":"description","value":"coffee"}])
.apply()
.then(function (res) {
assert.equal(201, res.statusCode);
return db.get(collection, key);
})
.then(function (res) {
assert.equal(200, res.statusCode);
assert.equal('test@test.com', res.body.info.email);
assert.equal('coffee', res.body.info.description);
return Q.resolve(res);
});
});
test('Nested Patch with builder', function () {
return db.newPatchBuilder(collection, key)
.patch("info", db.newPatchBuilder()
.add("description", "coffee")
)
.apply()
.then(function (res) {
assert.equal(201, res.statusCode);
return db.get(collection, key);
})
.then(function (res) {
assert.equal(200, res.statusCode);
assert.equal('test@test.com', res.body.info.email);
assert.equal('coffee', res.body.info.description);
return Q.resolve(res);
});
});
test('Nested Patch with conditional test', function () {
// conditional nested patches only apply if all the inner patch test ops pass
var nested = db.newPatchBuilder()
.test("email","test@test.com")
.add("description", "coffee");
return db.newPatchBuilder(collection, key)
.patch("info", nested, true)
.inc("age")
.apply()
.then(function (res) {
assert.equal(201, res.statusCode);
return db.get(collection, key);
})
.then(function (res) {
assert.equal(200, res.statusCode);
assert.equal('test@test.com', res.body.info.email);
assert.equal('coffee', res.body.info.description);
assert.equal(100, res.body.age);
return Q.resolve(res);
});
});
test('Nested Patch with failing conditional test does not apply', function () {
// conditional nested patches only apply if all the inner patch test ops pass
var nested = db.newPatchBuilder()
.test("email","foo") // this test op will not pass
.add("description", "coffee");
return db.newPatchBuilder(collection, key)
.patch("info", nested, true)
.inc("age")
.apply()
.then(function (res) {
assert.equal(201, res.statusCode);
return db.get(collection, key);
})
.then(function (res) {
assert.equal(200, res.statusCode);
assert.equal('test@test.com', res.body.info.email);
// description was NOT set because the conditional patch did not apply
assert(typeof res.body.info.description === 'undefined');
// but the inc op following the conditional nested patch DOES still apply
// because the conditional patch does not FAIL the overall patch.
assert.equal(100, res.body.age);
return Q.resolve(res);
});
});
test('Nested Patch via patchIf', function () {
return db.newPatchBuilder(collection, key)
// patchIf is just a convenience method to create a conditional nested patch
.patchIf("info", db.newPatchBuilder()
.test("email","foo")
.add("description", "coffee")
)
.inc("age")
.apply()
.then(function (res) {
assert.equal(201, res.statusCode);
return db.get(collection, key);
})
.then(function (res) {
assert.equal(200, res.statusCode);
assert.equal('test@test.com', res.body.info.email);
// description was NOT set because the conditional patch did not apply
assert(typeof res.body.info.description === 'undefined');
// but the inc op following the conditional nested patch DOES still apply
// because the conditional patch does not FAIL the overall patch.
assert.equal(100, res.body.age);
return Q.resolve(res);
});
});
test('Upsert with patch ops', function () {
var key2 = key + "_2";
return db.newPatchBuilder(collection, key2)
.add("email","test@test.com")
.upsert(true)
.apply()
.then(function (res) {
assert.equal(201, res.statusCode);
return db.get(collection, key2);
})
.then(function (res) {
assert.equal(200, res.statusCode);
// ONLY email is set, since this patch was an upsert to a new key.
assert.deepEqual({"email":"test@test.com"}, res.body);
return Q.resolve(res);
});
});
test('Upsert just applies as patch if item already present', function () {
return db.newPatchBuilder(collection, key)
.add("email","test@test.com")
.upsert(true)
.apply()
.then(function (res) {
assert.equal(201, res.statusCode);
return db.get(collection, key);
})
.then(function (res) {
assert.equal(200, res.statusCode);
assert.equal("test@test.com", res.body.email);
assert.equal(99, res.body.age);
return Q.resolve(res);
});
});
});