Implementation and tests for most Database methods.
Database.listTables has not been implemented yet because there is
something wrong with the way syncbase is implementing Glob. I'll
get to that in a later CL.
The batch methods have not been implemented yet, because the server does
not support them.
Change-Id: Ifcef4c885daa1ff4f4016e326b4c23ed4e2cecd9
diff --git a/Makefile b/Makefile
index f7f24a8..988c630 100644
--- a/Makefile
+++ b/Makefile
@@ -80,7 +80,7 @@
.NOTPARALLEL: test-integration
.PHONY: test-integration
-test: test-integration-browser test-integration-node
+test-integration: test-integration-browser test-integration-node
.PHONY: test-integration-node
test-integration-node: export PATH := ./test:$(PATH)
diff --git a/package.json b/package.json
index 7391470..1c20fd4 100644
--- a/package.json
+++ b/package.json
@@ -4,7 +4,8 @@
"description": "",
"main": "src/syncbase.js",
"dependencies": {
- "debug": "~2.2.0"
+ "debug": "~2.2.0",
+ "inherits": "~2.0.1"
},
"devDependencies": {
"jshint": "~2.7.0",
@@ -13,6 +14,7 @@
"run-parallel": "~1.1.1",
"which": "~1.1.1",
"minimist": "~1.1.1",
+ "xtend": "~4.0.0",
"async": "~1.0.0"
},
"repository": {
diff --git a/src/app.js b/src/app.js
index d4f9969..06b8b65 100644
--- a/src/app.js
+++ b/src/app.js
@@ -4,43 +4,37 @@
var vanadium = require('vanadium');
+var Database = require('./nosql/database').Database;
+var utils = require('./utils');
var vdl = require('./gen-vdl/v.io/syncbase/v23/services/syncbase');
var wireSignature = vdl.App.prototype._serviceDescription;
module.exports = App;
-function App(fullName, name) {
+function App(parentFullName, relativeName) {
if (!(this instanceof App)) {
- return new App(fullName, name);
+ return new App(parentFullName, relativeName);
}
- /**
- * @property name
- * @type {string}
- */
- Object.defineProperty(this, 'name', {
- value: name,
- writable: false,
- enumerable: true
- });
+ utils.addNameProperties(this, parentFullName, relativeName);
/**
- * @property name
- * @type {string}
+ * Caches the database wire object.
+ * @private
*/
- Object.defineProperty(this, 'fullName', {
- value: fullName,
- writable: false,
- enumerable: true
+ Object.defineProperty(this, '_wireObj', {
+ enumerable: false,
+ value: null,
+ writable: true
});
-
- this._wireObj = null;
}
// noSqlDatabase returns the noSqlDatabase with the given name. relativeName
// must not contain slashes.
-App.prototype.noSqlDatabase = function(ctx, relativeName) {};
+App.prototype.noSqlDatabase = function(relativeName) {
+ return new Database(this.fullName, relativeName);
+};
// listDatabases returns of all database names.
App.prototype.listDatabases = function(ctx) {};
@@ -72,4 +66,4 @@
}
return this._wireObj;
-};
\ No newline at end of file
+};
diff --git a/src/nosql/database.js b/src/nosql/database.js
index e75f5ac..ba51d48 100644
--- a/src/nosql/database.js
+++ b/src/nosql/database.js
@@ -2,105 +2,248 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-module.exports = Database;
+var vanadium = require('vanadium');
-// Database represents a collection of Tables. Batches, queries, sync, watch,
-// etc. all operate at the Database level.
-function Database(fullName, name) {
+var nosqlVdl = require('../gen-vdl/v.io/syncbase/v23/services/syncbase/nosql');
+var Table = require('./table');
+var utils = require('../utils');
+
+/**
+ * Database represents a collection of Tables. Batches, queries, sync, watch,
+ * etc. all operate at the Database level.
+ * @constructor
+ * @param {string} parentFullName Full name of App which contains this
+ * Database.
+ * @param {string} relativeName Relative name of this Database. Must not
+ * contain slashes.
+ */
+function Database(parentFullName, relativeName) {
if (!(this instanceof Database)) {
- return new Database(fullName, name);
+ return new Database(parentFullName, relativeName);
}
- /**
- * @property name
- * @type {string}
- */
- Object.defineProperty(this, 'name', {
- value: name,
- writable: false
- });
+ utils.addNameProperties(this, parentFullName, relativeName);
/**
- * @property name
- * @type {string}
+ * Caches the database wire object.
+ * @private
*/
- Object.defineProperty(this, 'fullName', {
- value: fullName,
- writable: false
+ Object.defineProperty(this, '_wireObj', {
+ enumerable: false,
+ value: null,
+ writable: true
});
}
-// Table returns the Table with the given name.
-// relativeName must not contain slashes.
-Database.prototype.table = function(relativeName) {};
+/**
+ * @private
+ */
+Database.prototype._wire = function(ctx) {
+ if (this._wireObj) {
+ return this._wireObj;
+ }
+ var client = vanadium.runtimeForContext(ctx).newClient();
+ var signature = [nosqlVdl.Database.prototype._serviceDescription];
-// ListTables returns a list of all Table names.
-Database.prototype.listTables = function(ctx) {};
+ this._wireObj = client.bindWithSignature(this.fullName, signature);
+ return this._wireObj;
+};
-// Create creates this Database.
-// If perms is nil, we inherit (copy) the App perms.
-Database.prototype.create = function(ctx, perms) {};
+/**
+ * Creates this Database.
+ * @param {module:vanadium.context.Context} ctx Vanadium context.
+ * @param {module:vanadium.security.access.Permissions} perms Permissions for
+ * the new database. If perms is null, we inherit (copy) the App perms.
+ * @param {function} cb Callback.
+ */
+Database.prototype.create = function(ctx, perms, cb) {
+ this._wire(ctx).create(ctx, perms, cb);
+};
-// Delete deletes this Database.
-Database.prototype.delete = function(ctx) {};
+/**
+ * Deletes this Database.
+ * @param {module:vanadium.context.Context} ctx Vanadium context.
+ * @param {function} cb Callback.
+ */
+Database.prototype.delete = function(ctx, cb) {
+ this._wire(ctx).delete(ctx, cb);
+};
-// Create creates the specified Table.
-// If perms is nil, we inherit (copy) the Database perms.
-// relativeName must not contain slashes.
-Database.prototype.createTable = function(ctx, relativeName, perms) {};
+/**
+ * Returns the Table with the given name.
+ * @param {string} relativeName Table name. Must not contain slashes.
+ * @return {module:syncbase.table.Table} Table object.
+ */
+Database.prototype.table = function(relativeName) {
+ return new Table(this.fullName, relativeName);
+};
-// DeleteTable deletes the specified Table.
-Database.prototype.deleteTable = function(ctx, relativeName) {};
+/**
+ * Returns a list of all Table names.
+ * @param {module:vanadium.context.Context} ctx Vanadium context.
+ * @param {function} cb Callback.
+ */
+Database.prototype.listTables = function(ctx, cb) {
+ return cb(new Error('not implemented'));
+};
-// SetPermissions replaces the current Permissions for an object.
-Database.prototype.setPermissions = function(ctx, perms, version) {};
+/**
+ * @private
+ */
+Database.prototype._tableWire = function(ctx, relativeName) {
+ if (relativeName.indexOf('/') >= 0) {
+ throw new Error('relativeName must not contain slashes.');
+ }
-// GetPermissions returns the current Permissions for an object.
-Database.prototype.getPermissions = function(ctx) {};
+ var client = vanadium.runtimeForContext(ctx).newClient();
+ var signature = [nosqlVdl.Table.prototype._serviceDescription];
-// BeginBatch creates a new batch. Instead of calling this function directly,
-// clients are recommended to use the RunInBatch() helper function, which
-// detects "concurrent batch" errors and handles retries internally.
-//
-// Default concurrency semantics:
-// - Reads inside a batch see a consistent snapshot, taken during
-// BeginBatch(), and will not see the effect of writes inside the batch.
-// - Commit() may fail with ErrConcurrentBatch, indicating that after
-// BeginBatch() but before Commit(), some concurrent routine wrote to a key
-// that matches a key or row-range read inside this batch. (Writes inside a
-// batch cannot cause that batch's Commit() to fail.)
-// - Other methods (e.g. Get) will never fail with error ErrConcurrentBatch,
-// even if it is known that Commit() will fail with this error.
-//
-// Concurrency semantics can be configured using BatchOptions.
-Database.prototype.beginBatch = function(ctx, opts) {};
+ var fullTableName = vanadium.naming.join(this.fullName, relativeName);
+ return client.bindWithSignature(fullTableName, signature);
+};
-// BatchDatabase is a handle to a set of reads and writes to the database that
-// should be considered an atomic unit. See BeginBatch() for concurrency
-// semantics.
+// TODO(nlacasse): It's strange that we create a Database with:
+// var db = new Database();
+// db.create();
+// But we create a Table with:
+// db.createTable();
+// The .delete method is similarly confusing. db.delete deletes a database,
+// but table.delete deletes a row (or row range).
+// Consider puting all 'create' and 'delete' methods on the parent class for
+// consistency.
+/**
+ * Creates the specified Table.
+ * If perms is nil, we inherit (copy) the Database perms.
+ * @param {module:vanadium.context.Context} ctx Vanadium context.
+ * @param {string} relativeName Table name. Must not contain slashes.
+ * @param {module:vanadium.security.access.Permissions} perms Permissions for
+ * the new database. If perms is null, we inherit (copy) the Database perms.
+ * @param {function} cb Callback.
+ */
+Database.prototype.createTable = function(ctx, relativeName, perms, cb) {
+ this._tableWire(ctx, relativeName).create(ctx, perms, cb);
+};
+
+/**
+ * Deletes the specified Table.
+ * @param {module:vanadium.context.Context} ctx Vanadium context.
+ * @param {string} relativeName Relative name of Table to delete. Must not
+ * contain slashes.
+ * @param {function} cb Callback.
+ */
+Database.prototype.deleteTable = function(ctx, relativeName, cb) {
+ this._tableWire(ctx, relativeName).delete(ctx, cb);
+};
+
+/**
+ * Replaces the current Permissions for the Database.
+ * @param {module:vanadium.context.Context} ctx Vanadium context.
+ * @param {module:vanadium.security.access.Permissions} perms Permissions for
+ * the database.
+ * @param {string} version Version of the current Permissions object which will
+ * be over-written. If empty, SetPermissions will perform an unconditional
+ * update.
+ * @param {function} cb Callback.
+ */
+Database.prototype.setPermissions = function(ctx, perms, version, cb) {
+ this._wire(ctx).setPermissions(ctx, perms, version, cb);
+};
+
+/**
+ * Returns the current Permissions for the Database.
+ * @param {module:vanadium.context.Context} ctx Vanadium context.
+ * @param {function} cb Callback.
+ */
+Database.prototype.getPermissions = function(ctx, cb) {
+ this._wire(ctx).getPermissions(ctx, cb);
+};
+
+/**
+ * Configuration options for Batches.
+ * @constructor
+ */
+var BatchOptions = nosqlVdl.BatchOptions;
+
+/**
+ * Creates a new batch. Instead of calling this function directly, clients are
+ * recommended to use the RunInBatch() helper function, which detects
+ * "concurrent batch" errors and handles retries internally.
+ *
+ * Default concurrency semantics:
+ * - Reads inside a batch see a consistent snapshot, taken during
+ * beginBatch(), and will not see the effect of writes inside the batch.
+ * - commit() may fail with errConcurrentBatch, indicating that after
+ * beginBatch() but before commit(), some concurrent routine wrote to a key
+ * that matches a key or row-range read inside this batch. (Writes inside a
+ * batch cannot cause that batch's commit() to fail.)
+ * - Other methods (e.g. get) will never fail with error errConcurrentBatch,
+ * even if it is known that commit() will fail with this error.
+ *
+ * Concurrency semantics can be configured using BatchOptions.
+ * @param {module:vanadium.context.Context} ctx Vanadium context.
+ * @param {module:vanadium.syncbase.database.BatchOptions} opts BatchOptions.
+ * @param {function} cb Callback.
+ */
+Database.prototype.beginBatch = function(ctx, opts, cb) {
+ cb(new Error('not implemented'));
+};
+
+/*
+ * A handle to a set of reads and writes to the database that should be
+ * considered an atomic unit. See beginBatch() for concurrency semantics.
+ * @constructor
+ * @param {module:vanadium.syncbase.database.Database} db Database.
+ */
function BatchDatabase(db) {
- if (typeof this !== BatchDatabase) {
+ if (!(this instanceof BatchDatabase)) {
return new BatchDatabase(db);
}
this._db = db;
+
+ throw new Error('not implemented');
}
-// Table returns the Table with the given name.
-// relativeName must not contain slashes.
-BatchDatabase.prototype.table = function(relativeName) {
- return this._db.table(relativeName);
+/**
+ * Returns the Table with the given name.
+ * @param {string} relativeName Table name. Must not contain slashes.
+ * @return {module:syncbase.table.Table} Table object.
+ */
+BatchDatabase.prototype.table = function(ctx, relativeName, cb) {
+ return this._db.table(ctx, relativeName, cb);
};
-// ListTables returns a list of all Table names.
-BatchDatabase.prototype.listTables = function(ctx) {
- return this._db.listTables(ctx);
+/**
+ * Returns a list of all Table names.
+ * @param {module:vanadium.context.Context} ctx Vanadium context.
+ * @param {function} cb Callback.
+ */
+BatchDatabase.prototype.listTables = function(ctx, cb) {
+ return this._db.listTables(ctx, cb);
};
-// Commit persists the pending changes to the database.
-BatchDatabase.prototype.commit = function(relativeName) {};
+/**
+ * Persists the pending changes to the database.
+ * @param {module:vanadium.context.Context} ctx Vanadium context.
+ * @param {function} cb Callback.
+ */
+BatchDatabase.prototype.commit = function(ctx, cb) {
+ cb(new Error('not implemented'));
+};
-// Abort notifies the server that any pending changes can be discarded.
-// It is not strictly required, but it may allow the server to release locks
-// or other resources sooner than if it was not called.
-BatchDatabase.prototype.abort = function(ctx) {};
\ No newline at end of file
+/**
+ * Notifies the server that any pending changes can be discarded. It is not
+ * strictly required, but it may allow the server to release locks or other
+ * resources sooner than if it was not called.
+ * @param {module:vanadium.context.Context} ctx Vanadium context.
+ * @param {function} cb Callback.
+ */
+BatchDatabase.prototype.abort = function(ctx, cb) {
+ cb(new Error('not implemented'));
+};
+
+module.exports = {
+ BatchDatabase: BatchDatabase,
+ BatchOptions: BatchOptions,
+ Database: Database
+};
diff --git a/src/nosql/table.js b/src/nosql/table.js
index 9510096..47dc45d 100644
--- a/src/nosql/table.js
+++ b/src/nosql/table.js
@@ -4,31 +4,15 @@
module.exports = Table;
+var utils = require('../utils');
+
// Table represents a collection of Rows.
-function Table(fullName, name) {
+function Table(parentFullName, relativeName) {
if (!(this instanceof Table)) {
- return new Table(fullName, name);
+ return new Table(parentFullName, relativeName);
}
- /**
- * The relative name of this Table.
- * @property name
- * @type {string}
- */
- Object.defineProperty(this, 'name', {
- value: name,
- writable: false
- });
-
- /**
- * The full name (object name) of this Table.
- * @property fullName
- * @type {string}
- */
- Object.defineProperty(this, 'fullName', {
- value: fullName,
- writable: false
- });
+ utils.addNameProperties(this, parentFullName, relativeName);
}
// Row returns the Row with the given primary key.
@@ -72,4 +56,4 @@
// DeletePermissions deletes the permissions for the specified prefix. Any
// rows covered by this prefix will use the next longest prefix's permissions.
-Table.prototype.deletePermissions = function(ctx, prefix) {};
\ No newline at end of file
+Table.prototype.deletePermissions = function(ctx, prefix) {};
diff --git a/src/service.js b/src/service.js
index 00f3a64..8874165 100644
--- a/src/service.js
+++ b/src/service.js
@@ -19,7 +19,7 @@
}
/**
- * @property name
+ * @property fullName
* @type {string}
*/
Object.defineProperty(this, 'fullName', {
@@ -28,14 +28,21 @@
enumerable: true
});
- this._wireObj = null;
+ /**
+ * Caches the database wire object.
+ * @private
+ */
+ Object.defineProperty(this, '_wireObj', {
+ enumerable: false,
+ value: null,
+ writable: true
+ });
}
// app returns the app with the given name. relativeName should not contain
// slashes.
Service.prototype.app = function(relativeName) {
- var fullName = vanadium.naming.join(this.fullName, relativeName);
- return new App(fullName, relativeName);
+ return new App(this.fullName, relativeName);
};
// listApps returns a list of all app names.
@@ -77,4 +84,4 @@
}
return this._wireObj;
-};
\ No newline at end of file
+};
diff --git a/src/utils.js b/src/utils.js
new file mode 100644
index 0000000..bee3858
--- /dev/null
+++ b/src/utils.js
@@ -0,0 +1,51 @@
+// Copyright 2015 The Vanadium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+var inherits = require('inherits');
+var vanadium = require('vanadium');
+
+module.exports = {
+ addNameProperties: addNameProperties,
+ InvalidNameError: InvalidNameError
+};
+
+/**
+ * Creates the 'name' and 'fullName' properties on an object.
+ * @private
+ */
+function addNameProperties(self, parentFullName, relativeName) {
+ if (relativeName.indexOf('/') >= 0) {
+ throw new InvalidNameError(relativeName);
+ }
+
+ var fullName = vanadium.naming.join(parentFullName, relativeName);
+
+ /**
+ * @property name
+ * @type {string}
+ */
+ Object.defineProperty(self, 'name', {
+ value: relativeName,
+ writable: false,
+ enumerable: true
+ });
+
+ /**
+ * @property name
+ * @type {string}
+ */
+ Object.defineProperty(self, 'fullName', {
+ value: fullName,
+ writable: false,
+ enumerable: true
+ });
+}
+
+function InvalidNameError(name) {
+ Error.call(this);
+ this.message = 'Invalid name "' + name + '". ' +
+ ' Use vanadium.naming.encodeAsNamePart() to escape.';
+}
+inherits(InvalidNameError, Error);
+
diff --git a/test/integration/app-name.js b/test/integration/app-name.js
deleted file mode 100644
index 0a7558c..0000000
--- a/test/integration/app-name.js
+++ /dev/null
@@ -1,21 +0,0 @@
-// Copyright 2015 The Vanadium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-
-/*
- * TODO(aghassemi) We want to have each test run in a completely clean state of
- * the syncbase but currently it is hard to do that since we would need to
- * restart a running service. For now we use a unique app name to isolate tests
- * but this is not the ideal approach.
- */
-
-/*
- * Provides unique names for the duration of a full testing runtime so that
- * each test can create its own unique apps in the singleton syncbase and
- * therefore be independent of other tests.
- */
-var counter = 0;
-module.exports = function() {
- return 'testApp_' + counter++;
-};
\ No newline at end of file
diff --git a/test/integration/service-name.js b/test/integration/service-name.js
index a463b6e..4c6431a 100644
--- a/test/integration/service-name.js
+++ b/test/integration/service-name.js
@@ -2,4 +2,4 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-module.exports = 'test/syncbased';
\ No newline at end of file
+module.exports = 'test/syncbased';
diff --git a/test/integration/test-app.js b/test/integration/test-app.js
index 6f43e94..3423de4 100644
--- a/test/integration/test-app.js
+++ b/test/integration/test-app.js
@@ -6,10 +6,12 @@
var test = require('prova');
var vanadium = require('vanadium');
-var getUniqueAppName = require('./app-name');
-var SERVICE_NAME = require('./service-name');
var syncbase = require('../..');
+var testUtils = require('./utils');
+var setup = testUtils.setupService;
+var uniqueName = testUtils.uniqueName;
+
var DEFAULT_PERMISSIONS = new Map([
['Read', {
'in': ['...'],
@@ -32,15 +34,11 @@
*/
test('Creating a service and checking its full name', function(t) {
- setup(t, function(err, o) {
- if (err) {
- return t.end(err);
- }
+ var mockServiceName = 'foo/bar/baz';
- var service = syncbase.newService(SERVICE_NAME);
- t.equals(service.fullName, SERVICE_NAME, 'Service name matches');
- o.teardown(t.end);
- });
+ var service = syncbase.newService(mockServiceName);
+ t.equals(service.fullName, mockServiceName, 'Service name matches');
+ t.end();
});
test('Getting a handle to an app', function(t) {
@@ -49,13 +47,12 @@
return t.end(err, 'Failed to setup');
}
- var APP_NAME = getUniqueAppName();
+ var appName = uniqueName('app');
- var service = syncbase.newService(SERVICE_NAME);
- var app = service.app(APP_NAME);
+ var app = o.service.app(appName);
- t.equals(app.name, APP_NAME, 'App name matches');
- t.equals(app.fullName, vanadium.naming.join(SERVICE_NAME, APP_NAME),
+ t.equals(app.name, appName, 'App name matches');
+ t.equals(app.fullName, vanadium.naming.join(o.service.fullName, appName),
'App full name matches');
o.teardown(t.end);
@@ -68,16 +65,14 @@
return t.end(err, 'Failed to setup');
}
- var service = syncbase.newService(SERVICE_NAME);
-
// Create multiple apps
var expectedAppNames = [
- getUniqueAppName(),
- getUniqueAppName(),
- getUniqueAppName()
+ uniqueName('app'),
+ uniqueName('app'),
+ uniqueName('app')
];
- createAppsAndVerifyExistance(t, service, o.ctx, expectedAppNames,
+ createAppsAndVerifyExistance(t, o.service, o.ctx, expectedAppNames,
function() {
o.teardown(t.end);
}
@@ -91,13 +86,12 @@
return t.end(err);
}
- var service = syncbase.newService(SERVICE_NAME);
- var appName = getUniqueAppName();
+ var appName = uniqueName('app');
- createAppsAndVerifyExistance(t, service, o.ctx, [appName], deleteApp);
+ createAppsAndVerifyExistance(t, o.service, o.ctx, [appName], deleteApp);
function deleteApp() {
- service.app(appName).delete(o.ctx, verifyItNoLongerExists);
+ o.service.app(appName).delete(o.ctx, verifyItNoLongerExists);
}
function verifyItNoLongerExists(err) {
@@ -106,7 +100,7 @@
return o.teardown(t.end);
}
- service.listApps(o.ctx, function(err, apps) {
+ o.service.listApps(o.ctx, function(err, apps) {
if (err) {
t.fail(err, 'Failed to list apps');
return o.teardown(t.end);
@@ -125,13 +119,13 @@
return t.end(err);
}
- var service = syncbase.newService(SERVICE_NAME);
- var appName = getUniqueAppName();
+ var appName = uniqueName('app');
- createAppsAndVerifyExistance(t, service, o.ctx, [appName], getPermissions);
+ createAppsAndVerifyExistance(t, o.service, o.ctx, [appName],
+ getPermissions);
function getPermissions() {
- service.app(appName).getPermissions(o.ctx, verifyPermissions);
+ o.service.app(appName).getPermissions(o.ctx, verifyPermissions);
}
function verifyPermissions(err, perms, version) {
@@ -158,8 +152,7 @@
return t.end(err);
}
- var service = syncbase.newService(SERVICE_NAME);
- var appName = getUniqueAppName();
+ var appName = uniqueName('app');
var NEW_PERMS = new Map([
['Read', {
'in': ['...', 'canRead'],
@@ -175,10 +168,11 @@
}]
]);
- createAppsAndVerifyExistance(t, service, o.ctx, [appName], setPermissions);
+ createAppsAndVerifyExistance(t, o.service, o.ctx, [appName],
+ setPermissions);
function setPermissions() {
- service.app(appName)
+ o.service.app(appName)
.setPermissions(o.ctx, NEW_PERMS, '0', getPermissions);
}
@@ -187,7 +181,7 @@
t.fail(err, 'Failed to set permissions for app');
return o.teardown(t.end);
}
- service.app(appName).getPermissions(o.ctx, verifyPermissions);
+ o.service.app(appName).getPermissions(o.ctx, verifyPermissions);
}
function verifyPermissions(err, perms, version) {
@@ -258,32 +252,3 @@
}
}
}
-
-// Helper function to create a Vanadium runtime and context.
-function setup(t, cb) {
- vanadium.init(function(err, rt) {
- if (err) {
- return cb(err);
- }
-
- var ctx = rt.getContext();
- var runtime = runtime;
-
- function teardown(cb) {
- rt.close(function(err) {
- if (err) {
- t.fail('Failed to close the runtime');
- return cb(err);
- }
-
- return cb();
- });
- }
-
- cb(null, {
- ctx: ctx,
- runtime: runtime,
- teardown: teardown
- });
- });
-}
\ No newline at end of file
diff --git a/test/integration/test-database.js b/test/integration/test-database.js
new file mode 100644
index 0000000..acf06ac
--- /dev/null
+++ b/test/integration/test-database.js
@@ -0,0 +1,223 @@
+// Copyright 2015 The Vanadium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+var naming = require('vanadium').naming;
+var test = require('prova');
+var vanadium = require('vanadium');
+
+var Database = require('../../src/nosql/database').Database;
+var Table = require('../../src/nosql/table');
+
+var testUtils = require('./utils');
+var uniqueName = testUtils.uniqueName;
+var setupApp = testUtils.setupApp;
+var setupDatabase = testUtils.setupDatabase;
+
+test('app.noSqlDatabase() returns a database', function(t) {
+ setupApp(t, function(err, o) {
+ if (err) {
+ return t.end(err);
+ }
+
+ var dbName = uniqueName('db');
+ var db = o.app.noSqlDatabase(dbName);
+
+ t.ok(db, 'Database is constructed.');
+ t.ok(db instanceof Database, 'database is a Database object.');
+ t.equal(db.name, dbName, 'Database has the correct name.');
+
+ db.name = 'foo';
+ t.equal(db.name, dbName, 'Setting the name has no effect.');
+
+ var expectedFullName = naming.join(o.app.fullName, dbName);
+ t.equal(db.fullName, expectedFullName, 'Database has correct fullName.');
+
+ db.fullName = 'bar';
+ t.equal(db.fullName, expectedFullName, 'Setting fullName has no effect.');
+
+ o.teardown(t.end);
+ });
+});
+
+test('app.noSqlDatabase with slashes in the name', function (t) {
+ setupApp(t, function(err, o) {
+ if (err) {
+ return t.end(err);
+ }
+
+ var dbName = 'bad/name';
+ t.throws(function() {
+ o.app.noSqlDatabase(dbName);
+ }, 'should throw');
+
+ o.teardown(t.end);
+ });
+});
+
+test('db.create() creates a database', function(t) {
+ setupApp(t, function(err, o) {
+ if (err) {
+ return t.end(err);
+ }
+
+ var db = o.app.noSqlDatabase(uniqueName('db'));
+
+ db.create(o.ctx, {}, function(err) {
+ t.error(err);
+
+ // TODO(nlacasse): Verify that the database exists using
+ // app.listDatabases(), once that function has been implemented.
+
+ o.teardown(t.end);
+ });
+ });
+});
+
+test('creating a database twice should error', function(t) {
+ setupApp(t, function(err, o) {
+ if (err) {
+ return t.end(err);
+ }
+
+ var db = o.app.noSqlDatabase(uniqueName('db'));
+
+ // Create once.
+ db.create(o.ctx, {}, function(err) {
+ if (err) {
+ t.error(err);
+ return o.teardown(t.end);
+ }
+
+ // Create again.
+ db.create(o.ctx, {}, function(err) {
+ t.ok(err, 'should error.');
+ o.teardown(t.end);
+ });
+ });
+ });
+});
+
+test('db.delete() deletes a database', function(t) {
+ setupApp(t, function(err, o) {
+ if (err) {
+ return t.end(err);
+ }
+
+ var db = o.app.noSqlDatabase(uniqueName('db'));
+
+ db.create(o.ctx, {}, function(err) {
+ if (err) {
+ t.error(err);
+ return o.teardown(t.end);
+ }
+
+ db.delete(o.ctx, function(err) {
+ t.error(err);
+
+ // TODO(nlacasse): Verify that the database no longer exists using
+ // app.listDatabases(), once that function has been implemented.
+
+ o.teardown(t.end);
+ });
+ });
+ });
+});
+
+test('deleting a db that has not been created should error', function(t) {
+ setupApp(t, function(err, o) {
+ if (err) {
+ return t.end(err);
+ }
+
+ var db = o.app.noSqlDatabase(uniqueName('db'));
+
+ db.delete(o.ctx, function(err) {
+ t.ok(err, 'should error');
+ o.teardown(t.end);
+ });
+ });
+});
+
+test('db.table() returns a table', function(t) {
+ setupApp(t, function(err, o) {
+ if (err) {
+ return t.end(err);
+ }
+
+ var db = o.app.noSqlDatabase(uniqueName('db'));
+ var tableName = uniqueName('table');
+ var table = db.table(tableName);
+
+ t.ok(table, 'table is created.');
+ t.ok(table instanceof Table, 'table is a Table object.');
+ t.equal(table.name, tableName, 'table has the correct name.');
+ t.equal(table.fullName, vanadium.naming.join(db.fullName, tableName),
+ 'table has the correct fullName.');
+
+ o.teardown(t.end);
+ });
+});
+
+test('db.createTable() creates a table', function(t) {
+ setupDatabase(t, function(err, o) {
+ if (err) {
+ return t.end(err);
+ }
+
+ var db = o.database;
+
+ var tableName = uniqueName('table');
+ db.createTable(o.ctx, tableName, {}, function(err) {
+ t.error(err);
+
+ // TODO(nlacasse): Verify that the table exists using db.listTables(),
+ // once that function has been implemented.
+
+ o.teardown(t.end);
+ });
+ });
+});
+
+test('db.deleteTable() deletes a table', function(t) {
+ setupDatabase(t, function(err, o) {
+ if (err) {
+ return t.end(err);
+ }
+
+ var db = o.database;
+
+ var tableName = uniqueName('table');
+ db.createTable(o.ctx, tableName, {}, function(err) {
+ if (err) {
+ t.error(err);
+ return o.teardown(t.end);
+ }
+
+ db.deleteTable(o.ctx, tableName, function(err) {
+ t.error(err);
+
+ // TODO(nlacasse): Verify that the table no longer exists using
+ // db.listTables(), once that function has been implemented.
+
+ o.teardown(t.end);
+ });
+ });
+ });
+});
+
+test('deleting a table that does not exist should error', function(t) {
+ setupDatabase(t, function(err, o) {
+ if (err) {
+ return t.end(err);
+ }
+
+ var db = o.database;
+ var tableName = uniqueName('table');
+
+ db.deleteTable(o.ctx, tableName, function(err) {
+ t.ok(err, 'should error.');
+ o.teardown(t.end);
+ });
+ });
+});
diff --git a/test/integration/utils.js b/test/integration/utils.js
new file mode 100644
index 0000000..89cf8c5
--- /dev/null
+++ b/test/integration/utils.js
@@ -0,0 +1,93 @@
+// Copyright 2015 The Vanadium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+var vanadium = require('vanadium');
+var extend = require('xtend');
+
+var syncbase = require('../..');
+
+var SERVICE_NAME = require('./service-name');
+
+// Helper function to generate unique names.
+var uniqueName = (function() {
+ var i = 0;
+ return function(prefix) {
+ prefix = prefix || 'name';
+ i++;
+ return prefix + '_' + i;
+ };
+})();
+
+// Initializes Vanadium runtime.
+function setupService(t, cb) {
+ vanadium.init(function(err, rt) {
+ if (err) {
+ return cb(err);
+ }
+
+ function teardown(cb) {
+ rt.close(function(err) {
+ t.error(err, 'rt.close should not error.');
+ cb(null);
+ });
+ }
+
+ var service = syncbase.newService(SERVICE_NAME);
+
+ return cb(null, {
+ ctx: rt.getContext(),
+ rt: rt,
+ service: service,
+ teardown: teardown
+ });
+ });
+}
+
+// Initializes Vanadium runtime and creates an App.
+function setupApp(t, cb) {
+ setupService(t, function(err, o) {
+ if (err) {
+ return cb(err);
+ }
+
+ var app = o.service.app(uniqueName('app'));
+
+ app.create(o.ctx, {}, function(err) {
+ if (err) {
+ o.rt.close(t.error);
+ return cb(err);
+ }
+
+ return cb(null, extend(o, {
+ app:app
+ }));
+ });
+ });
+}
+
+// Initializes Vanadium runtime and creates an App and a Database.
+function setupDatabase(t, cb) {
+ setupApp(t, function(err, o) {
+
+ var db = o.app.noSqlDatabase(uniqueName('db'));
+
+ db.create(o.ctx, {}, function(err) {
+ if (err) {
+ o.rt.close(t.error);
+ return cb(err);
+ }
+
+ return cb(null, extend(o, {
+ database: db
+ }));
+ });
+ });
+}
+
+module.exports = {
+ setupApp: setupApp,
+ setupDatabase: setupDatabase,
+ setupService: setupService,
+ uniqueName: uniqueName
+};
diff --git a/test/start-syncbased.sh b/test/start-syncbased.sh
index f4ac91f..cf54fb0 100755
--- a/test/start-syncbased.sh
+++ b/test/start-syncbased.sh
@@ -10,4 +10,4 @@
# fix service-runner to allow flags/arguments, and then have it start syncbased
# directly with the appropriate flags. Then we can delete this file.
-syncbased --name test/syncbased
+syncbased -v=3 --name test/syncbased