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