diff --git a/test/datastore/dataset.js b/test/datastore/dataset.js index 90bad497befa..757b05fac0c4 100644 --- a/test/datastore/dataset.js +++ b/test/datastore/dataset.js @@ -19,12 +19,9 @@ 'use strict'; var assert = require('assert'); -var ByteBuffer = require('bytebuffer'); -var gcloud = require('../../lib'); var datastore = require('../../lib').datastore; -var entity = require('../../lib/datastore/entity.js'); -var mockRespGet = require('../testdata/response_get.json'); -var Transaction = require('../../lib/datastore/transaction.js'); +var gcloud = require('../../lib'); +var util = require('../../lib/common/util.js'); describe('Dataset', function() { it('should not require connection details', function() { @@ -92,175 +89,53 @@ describe('Dataset', function() { }); }); - it('should get by key', function(done) { - var ds = datastore.dataset({ projectId: 'test' }); - ds.createRequest_ = function(method, proto, typ, callback) { - assert.equal(method, 'lookup'); - assert.equal(proto.key.length, 1); - callback(null, mockRespGet); - }; - ds.get(ds.key(['Kind', 123]), function(err, entity) { - var data = entity.data; - assert.deepEqual(entity.key.path, ['Kind', 5732568548769792]); - assert.strictEqual(data.author, 'Silvano'); - assert.strictEqual(data.isDraft, false); - assert.deepEqual(data.publishedAt, new Date(978336000000)); - done(); - }); - }); - - it('should multi get by keys', function(done) { - var ds = datastore.dataset({ projectId: 'test' }); - ds.createRequest_ = function(method, proto, typ, callback) { - assert.equal(method, 'lookup'); - assert.equal(proto.key.length, 1); - callback(null, mockRespGet); - }; - var key = ds.key(['Kind', 5732568548769792]); - ds.get([key], function(err, entities) { - var entity = entities[0]; - var data = entity.data; - assert.deepEqual(entity.key.path, ['Kind', 5732568548769792]); - assert.strictEqual(data.author, 'Silvano'); - assert.strictEqual(data.isDraft, false); - assert.deepEqual(data.publishedAt, new Date(978336000000)); - done(); - }); - }); - - it('should continue looking for deferred results', function(done) { - var ds = datastore.dataset({ projectId: 'test' }); - var key = ds.key(['Kind', 5732568548769792]); - var key2 = ds.key(['Kind', 5732568548769792]); - var lookupCount = 0; - ds.createRequest_ = function(method, proto, typ, callback) { - lookupCount++; - assert.equal(method, 'lookup'); - if (mockRespGet.deferred.length) { - // Revert deferred to original state. - mockRespGet.deferred = []; - } else { - mockRespGet.deferred = [ entity.keyToKeyProto(key2) ]; - } - callback(null, mockRespGet); - }; - ds.get([key, key2], function(err, entities) { - assert.equal(entities.length, 2); - assert.equal(lookupCount, 2); - done(); - }); - }); - - it('should delete by key', function(done) { - var ds = datastore.dataset({ projectId: 'test' }); - ds.createRequest_ = function(method, proto, typ, callback) { - assert.equal(method, 'commit'); - assert.equal(!!proto.mutation.delete, true); - callback(); - }; - ds.delete(ds.key(['Kind', 123]), done); - }); - - it('should multi delete by keys', function(done) { - var ds = datastore.dataset({ projectId: 'test' }); - ds.createRequest_ = function(method, proto, typ, callback) { - assert.equal(method, 'commit'); - assert.equal(proto.mutation.delete.length, 2); - callback(); - }; - ds.delete([ - ds.key(['Kind', 123]), - ds.key(['Kind', 345]) - ], done); - }); - - it('should save with incomplete key', function(done) { - var ds = datastore.dataset({ projectId: 'test' }); - ds.createRequest_ = function(method, proto, typ, callback) { - assert.equal(method, 'commit'); - assert.equal(proto.mutation.insert_auto_id.length, 1); - callback(); - }; - var key = ds.key('Kind'); - ds.save({ key: key, data: {} }, done); - }); - - it('should save with keys', function(done) { - var ds = datastore.dataset({ projectId: 'test' }); - ds.createRequest_ = function(method, proto, typ, callback) { - assert.equal(method, 'commit'); - assert.equal(proto.mutation.upsert.length, 2); - assert.equal(proto.mutation.upsert[0].property[0].name, 'k'); - assert.equal( - proto.mutation.upsert[0].property[0].value.string_value, 'v'); - callback(); - }; - ds.save([ - { key: ds.key(['Kind', 123]), data: { k: 'v' } }, - { key: ds.key(['Kind', 456]), data: { k: 'v' } } - ], done); - }); - - it('should produce proper allocate IDs req protos', function(done) { - var ds = datastore.dataset({ projectId: 'test' }); - ds.createRequest_ = function(method, proto, typ, callback) { - assert.equal(method, 'allocateIds'); - assert.equal(proto.key.length, 1); - assert.deepEqual(proto.key[0], { - partition_id: null, - path_element :[{kind:'Kind', name: null, id: null}] - }); - callback(null, { - key: [ - { path_element: [{ kind: 'Kind', id: 123 }] } - ] - }); - }; - ds.allocateIds(ds.key('Kind'), 1, function(err, ids) { - assert.deepEqual(ids[0], ds.key(['Kind', 123])); - done(); - }); - }); - - it('should throw if trying to allocate IDs with complete keys', function() { - var ds = datastore.dataset({ projectId: 'test' }); - assert.throws(function() { - ds.allocateIds(ds.key(['Kind', 123])); - }); - }); - describe('runInTransaction', function() { var ds; - var transaction; beforeEach(function() { ds = datastore.dataset({ projectId: 'test' }); + }); + + it('should begin transaction', function(done) { ds.createTransaction_ = function() { - transaction = new Transaction(); - transaction.createRequest_ = function(method, proto, typ, callback) { - assert.equal(method, 'beginTransaction'); - callback(null, { transaction: '' }); + return { + begin: function() { + done(); + } }; - return transaction; }; + ds.runInTransaction(); }); - it('should begin transaction', function() { - ds.runInTransaction(function() {}, function() {}); - }); - - it('should return transaction object to the callback', function() { - ds.runInTransaction(function(transactionObject) { - assert.equal(transactionObject, transaction); + it('should return transaction object to the callback', function(done) { + var transaction = { + begin: function(callback) { + callback(); + }, + finalize: util.noop + }; + ds.createTransaction_ = function() { + return transaction; + }; + ds.runInTransaction(function(t) { + assert.deepEqual(t, transaction); + done(); }, assert.ifError); }); - it('should commit the transaction when done', function() { - ds.runInTransaction(function(t, done) { - transaction.createRequest_ = function(method) { - assert.equal(method, 'commit'); + it('should return correct done function to the callback', function(done) { + ds.createTransaction_ = function() { + return { + begin: function(callback) { + callback(); + }, + finalize: function() { + done(); + } }; - done(); + }; + ds.runInTransaction(function(t, tDone) { + tDone(); }, assert.ifError); }); }); @@ -300,80 +175,4 @@ describe('Dataset', function() { assert.strictEqual(query.namespace, null); }); }); - - describe('runQuery', function() { - var ds; - var query; - var mockResponse = { - withResults: { - batch: { entity_result: mockRespGet.found } - }, - withResultsAndEndCursor: { - batch: { - entity_result: mockRespGet.found, - end_cursor: new ByteBuffer().writeIString('cursor').flip() - } - }, - withoutResults: mockRespGet - }; - - beforeEach(function() { - ds = datastore.dataset({ projectId: 'test' }); - query = ds.createQuery('Kind'); - }); - - describe('errors', function() { - it('should handle upstream errors', function() { - var upstreamError = new Error('upstream error.'); - ds.createRequest_ = function(method, proto, typ, callback) { - assert.equal(method, 'runQuery'); - callback(upstreamError); - }; - - ds.runQuery(query, function(err) { - assert.equal(err, upstreamError); - }); - }); - - it('should handle missing results error', function() { - ds.createRequest_ = function(method, proto, typ, callback) { - assert.equal(method, 'runQuery'); - callback('simulated-error', mockResponse.withoutResults); - }; - - ds.runQuery(query, function(err) { - assert.equal(err, 'simulated-error'); - }); - }); - }); - - it('should execute callback with results', function() { - ds.createRequest_ = function(method, proto, typ, callback) { - assert.equal(method, 'runQuery'); - callback(null, mockResponse.withResults); - }; - - ds.runQuery(query, function (err, entities) { - assert.ifError(err); - assert.deepEqual(entities[0].key.path, ['Kind', 5732568548769792]); - - var data = entities[0].data; - assert.strictEqual(data.author, 'Silvano'); - assert.strictEqual(data.isDraft, false); - assert.deepEqual(data.publishedAt, new Date(978336000000)); - }); - }); - - it('should return a new query if results remain', function() { - ds.createRequest_ = function(method, proto, typ, callback) { - assert.equal(method, 'runQuery'); - callback(null, mockResponse.withResultsAndEndCursor); - }; - - ds.runQuery(query, function(err, entities, nextQuery) { - assert.ifError(err); - assert.equal(nextQuery.constructor.name, 'Query'); - }); - }); - }); }); diff --git a/test/datastore/request.js b/test/datastore/request.js new file mode 100644 index 000000000000..e7d147d2db60 --- /dev/null +++ b/test/datastore/request.js @@ -0,0 +1,392 @@ +/** + * Copyright 2014 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/*global describe, it, beforeEach */ + +'use strict'; + +var assert = require('assert'); +var ByteBuffer = require('bytebuffer'); +var entity = require('../../lib/datastore/entity.js'); +var mockRespGet = require('../testdata/response_get.json'); +var Query = require('../../lib/datastore/query.js'); +var through = require('through2'); +var util = require('../../lib/common/util.js'); + +var httpsRequestOverride = util.noop; +var https = { + request: function() { + var result = httpsRequestOverride.apply(this, util.toArray(arguments)); + httpsRequestOverride = util.noop; + return result; + } +}; + +var Request = require('sandboxed-module') + .require('../../lib/datastore/request.js', { + requires: { + 'https': https + } + }); + +describe('Request', function() { + var key; + var request; + + beforeEach(function() { + key = new entity.Key({ + namespace: 'namespace', + path: ['Company', 123] + }); + request = new Request(); + }); + + describe('get', function() { + it('should get by key', function(done) { + request.createRequest_ = function(method, proto, typ, callback) { + assert.equal(method, 'lookup'); + assert.equal(proto.key.length, 1); + callback(null, mockRespGet); + }; + request.get(key, function(err, entity) { + var data = entity.data; + assert.deepEqual(entity.key.path, ['Kind', 5732568548769792]); + assert.strictEqual(data.author, 'Silvano'); + assert.strictEqual(data.isDraft, false); + assert.deepEqual(data.publishedAt, new Date(978336000000)); + done(); + }); + }); + + it('should multi get by keys', function(done) { + request.createRequest_ = function(method, proto, typ, callback) { + assert.equal(method, 'lookup'); + assert.equal(proto.key.length, 1); + callback(null, mockRespGet); + }; + request.get([key], function(err, entities) { + var entity = entities[0]; + var data = entity.data; + assert.deepEqual(entity.key.path, ['Kind', 5732568548769792]); + assert.strictEqual(data.author, 'Silvano'); + assert.strictEqual(data.isDraft, false); + assert.deepEqual(data.publishedAt, new Date(978336000000)); + done(); + }); + }); + + it('should continue looking for deferred results', function(done) { + var lookupCount = 0; + request.createRequest_ = function(method, proto, typ, callback) { + lookupCount++; + assert.equal(method, 'lookup'); + if (mockRespGet.deferred.length) { + // Revert deferred to original state. + mockRespGet.deferred = []; + } else { + mockRespGet.deferred = [ entity.keyToKeyProto(key) ]; + } + callback(null, mockRespGet); + }; + request.get([key, key], function(err, entities) { + assert.equal(entities.length, 2); + assert.equal(lookupCount, 2); + done(); + }); + }); + }); + + describe('save', function() { + it('should save with incomplete key', function(done) { + request.createRequest_ = function(method, proto, typ, callback) { + assert.equal(method, 'commit'); + assert.equal(proto.mutation.insert_auto_id.length, 1); + callback(); + }; + var key = new entity.Key({ namespace: 'ns', path: ['Company'] }); + request.save({ key: key, data: {} }, done); + }); + + it('should save with keys', function(done) { + request.createRequest_ = function(method, proto, typ, callback) { + assert.equal(method, 'commit'); + assert.equal(proto.mutation.upsert.length, 2); + assert.equal(proto.mutation.upsert[0].property[0].name, 'k'); + assert.equal( + proto.mutation.upsert[0].property[0].value.string_value, 'v'); + callback(); + }; + request.save([ + { key: key, data: { k: 'v' } }, + { key: key, data: { k: 'v' } } + ], done); + }); + + describe('transactions', function() { + beforeEach(function() { + // Trigger transaction mode. + request.id = 'transaction-id'; + }); + + it('should mark transaction as finalized', function(done) { + assert.strictEqual(request.isFinalized, undefined); + request.createRequest_ = function(method, proto, typ, callback) { + callback(null, { mutation_result: {} }); + }; + request.save({ key: key, data: {} }, function(err) { + assert.ifError(err); + assert.strictEqual(request.isFinalized, true); + done(); + }); + }); + + it('should not mark as finalized if an error occurred', function(done) { + assert.strictEqual(request.isFinalized, undefined); + request.createRequest_ = function(method, proto, typ, callback) { + callback(new Error('Error.')); + }; + request.save({ key: key, data: {} }, function() { + assert.strictEqual(request.isFinalized, undefined); + done(); + }); + }); + + it('should not set an indexed value by default', function(done) { + request.createRequest_ = function(method, proto) { + var property = proto.mutation.upsert[0].property[0]; + assert.equal(property.name, 'name'); + assert.equal(property.value.string_value, 'value'); + assert.strictEqual(property.value.indexed, null); + done(); + }; + request.save({ + key: key, + data: [{ name: 'name', value: 'value' }] + }, assert.ifError); + }); + + it('should allow setting the indexed value of property', function(done) { + request.createRequest_ = function(method, proto) { + var property = proto.mutation.upsert[0].property[0]; + assert.equal(property.name, 'name'); + assert.equal(property.value.string_value, 'value'); + assert.strictEqual(property.value.indexed, false); + done(); + }; + request.save({ + key: key, + data: [{ name: 'name', value: 'value', excludeFromIndexes: true }] + }, assert.ifError); + }); + }); + }); + + describe('delete', function() { + it('should delete by key', function(done) { + request.createRequest_ = function(method, proto, typ, callback) { + assert.equal(method, 'commit'); + assert.equal(!!proto.mutation.delete, true); + callback(); + }; + request.delete(key, done); + }); + + it('should multi delete by keys', function(done) { + request.createRequest_ = function(method, proto, typ, callback) { + assert.equal(method, 'commit'); + assert.equal(proto.mutation.delete.length, 2); + callback(); + }; + request.delete([ key, key ], done); + }); + + describe('transactions', function() { + beforeEach(function() { + // Trigger transaction mode. + request.id = 'transaction-id'; + }); + + it('should mark transaction as finalized', function(done) { + assert.strictEqual(request.isFinalized, undefined); + request.createRequest_ = function(method, proto, typ, callback) { + callback(null, { mutation_result: {} }); + }; + request.delete(key, function(err) { + assert.ifError(err); + assert.strictEqual(request.isFinalized, true); + done(); + }); + }); + + it('should not mark as finalized if an error occurred', function(done) { + assert.strictEqual(request.isFinalized, undefined); + request.createRequest_ = function(method, proto, typ, callback) { + callback(new Error('Error.')); + }; + request.delete(key, function() { + assert.strictEqual(request.isFinalized, undefined); + done(); + }); + }); + }); + }); + + describe('runQuery', function() { + var query; + var mockResponse = { + withResults: { + batch: { entity_result: mockRespGet.found } + }, + withResultsAndEndCursor: { + batch: { + entity_result: mockRespGet.found, + end_cursor: new ByteBuffer().writeIString('cursor').flip() + } + }, + withoutResults: mockRespGet + }; + + beforeEach(function() { + query = new Query(['Kind']); + }); + + describe('errors', function() { + it('should handle upstream errors', function() { + var error = new Error('Error.'); + request.createRequest_ = function(method, proto, typ, callback) { + assert.equal(method, 'runQuery'); + callback(error); + }; + + request.runQuery(query, function(err) { + assert.equal(err, error); + }); + }); + + it('should handle missing results error', function() { + request.createRequest_ = function(method, proto, typ, callback) { + assert.equal(method, 'runQuery'); + callback(null, mockResponse.withoutResults); + }; + + request.runQuery(query, function(err, entities) { + assert.strictEqual(err, null); + assert.strictEqual(entities, undefined); + }); + }); + }); + + it('should execute callback with results', function() { + request.createRequest_ = function(method, proto, typ, callback) { + assert.equal(method, 'runQuery'); + callback(null, mockResponse.withResults); + }; + + request.runQuery(query, function (err, entities) { + assert.ifError(err); + assert.deepEqual(entities[0].key.path, ['Kind', 5732568548769792]); + + var data = entities[0].data; + assert.strictEqual(data.author, 'Silvano'); + assert.strictEqual(data.isDraft, false); + assert.deepEqual(data.publishedAt, new Date(978336000000)); + }); + }); + + it('should return a new query if results remain', function() { + request.createRequest_ = function(method, proto, typ, callback) { + assert.equal(method, 'runQuery'); + callback(null, mockResponse.withResultsAndEndCursor); + }; + + request.runQuery(query, function(err, entities, nextQuery) { + assert.ifError(err); + assert.equal(nextQuery.constructor.name, 'Query'); + }); + }); + }); + + describe('allocateIds', function() { + it('should produce proper allocate IDs req protos', function(done) { + request.createRequest_ = function(method, proto, typ, callback) { + assert.equal(method, 'allocateIds'); + assert.equal(proto.key.length, 1); + assert.deepEqual(proto.key[0], { + partition_id: null, + path_element :[{ kind: 'Kind', name: null, id: null }] + }); + callback(null, { + key: [ + { path_element: [{ kind: 'Kind', id: 123 }] } + ] + }); + }; + var incompleteKey = new entity.Key({ namespace: null, path: ['Kind'] }); + request.allocateIds(incompleteKey, 1, function(err, keys) { + assert.ifError(err); + var generatedKey = keys[0]; + assert.strictEqual(generatedKey.path.pop(), 123); + done(); + }); + }); + + it('should throw if trying to allocate IDs with complete keys', function() { + assert.throws(function() { + request.allocateIds(key); + }); + }); + }); + + describe('createRequest_', function() { + var mockProtoRequest = { fake: 'request' }; + mockProtoRequest.toBuffer = function() { + return mockProtoRequest; + }; + + beforeEach(function() { + request.connection = { + createAuthorizedReq: util.noop + }; + }); + + it('should assemble correct request', function(done) { + var method = 'commit'; + var projectId = 'project-id'; + request.projectId = projectId; + request.connection.createAuthorizedReq = function(opts) { + assert.equal(opts.method, 'POST'); + assert.equal( + opts.path, '/datastore/v1beta2/datasets/' + projectId + '/' + method); + assert.equal(opts.headers['content-type'], 'application/x-protobuf'); + done(); + }; + request.createRequest_(method, null, null, util.noop); + }); + + it('should make https request', function(done) { + var mockRequest = { fake: 'request' }; + httpsRequestOverride = function(req) { + assert.deepEqual(req, mockRequest); + done(); + return through(); + }; + request.connection.createAuthorizedReq = function(opts, callback) { + callback(null, mockRequest); + }; + request.createRequest_('commit', mockProtoRequest, null, util.noop); + }); + }); +}); diff --git a/test/datastore/transaction.js b/test/datastore/transaction.js index fd1e241b95e3..dc3d87f400e4 100644 --- a/test/datastore/transaction.js +++ b/test/datastore/transaction.js @@ -19,115 +19,156 @@ 'use strict'; var assert = require('assert'); -var datastore = require('../../lib').datastore; -var Key = require('../../lib/datastore/entity').Key; +var Transaction = require('../../lib/datastore/transaction.js'); describe('Transaction', function() { - var ds; var transaction; + var TRANSACTION_ID = 'transaction-id'; beforeEach(function() { - ds = datastore.dataset({ projectId: 'test' }); - transaction = ds.createTransaction_(null, 'test'); + transaction = new Transaction(null, 'project-id'); }); describe('begin', function() { it('should begin', function(done) { - transaction.createRequest_ = function(method, proto, respType, callback) { + transaction.createRequest_ = function(method) { assert.equal(method, 'beginTransaction'); - callback(null, 'some-id'); + done(); + }; + transaction.begin(); + }); + + it('should set transaction id', function(done) { + transaction.createRequest_ = function(method, proto, respType, callback) { + callback(null, { transaction: TRANSACTION_ID }); + }; + transaction.begin(function(err) { + assert.ifError(err); + assert.equal(transaction.id, TRANSACTION_ID); + done(); + }); + }); + + it('should pass error to callback', function(done) { + var error = new Error('Error.'); + transaction.createRequest_ = function(method, proto, respType, callback) { + callback(error); }; - transaction.begin(done); + transaction.begin(function(err) { + assert.deepEqual(err, error); + done(); + }); }); }); describe('rollback', function() { beforeEach(function() { - transaction.id = 'some-id'; + transaction.id = TRANSACTION_ID; }); it('should rollback', function(done) { - transaction.createRequest_ = function(method, proto, respType, callback) { + transaction.createRequest_ = function(method, proto) { assert.equal(method, 'rollback'); assert.equal( proto.transaction.toBase64(), new Buffer(transaction.id).toString('base64')); + done(); + }; + transaction.rollback(); + }); + + it('should pass error to callback', function(done) { + var error = new Error('Error.'); + transaction.createRequest_ = function(method, proto, respType, callback) { + callback(error); + }; + transaction.rollback(function(err) { + assert.deepEqual(err, error); + done(); + }); + }); + + it('should mark as finalized', function(done) { + transaction.createRequest_ = function(method, proto, respType, callback) { callback(); }; transaction.rollback(function() { - assert.equal(transaction.isFinalized, true); + assert.strictEqual(transaction.isFinalized, true); done(); }); }); - it('should mark as `finalized` when rollback errors', function(done) { - var error = new Error('rollback error'); + it('should mark as finalized when rollback errors', function(done) { transaction.createRequest_ = function(method, proto, respType, callback) { - callback(error); + callback(new Error('Error.')); }; - transaction.rollback(function(err) { - assert.equal(err, error); - assert.equal(transaction.isFinalized, true); + transaction.rollback(function() { + assert.strictEqual(transaction.isFinalized, true); done(); }); }); }); describe('commit', function() { + beforeEach(function() { + transaction.id = TRANSACTION_ID; + }); + it('should commit', function(done) { - transaction.id = 'some-id'; - transaction.createRequest_ = function(method, proto, respType, callback) { + transaction.createRequest_ = function(method, proto) { assert.equal(method, 'commit'); assert.equal( proto.transaction.toBase64(), - new Buffer('some-id').toString('base64')); + new Buffer(transaction.id).toString('base64')); + done(); + }; + transaction.commit(); + }); + + it('should pass error to callback', function(done) { + var error = new Error('Error.'); + transaction.createRequest_ = function(method, proto, respType, callback) { + callback(error); + }; + transaction.commit(function(err) { + assert.deepEqual(err, error); + done(); + }); + }); + + it('should mark as finalized', function(done) { + transaction.createRequest_ = function(method, proto, respType, callback) { callback(); }; transaction.commit(function() { - assert.equal(transaction.isFinalized, true); + assert.strictEqual(transaction.isFinalized, true); done(); }); }); - }); - describe('finalize', function() { - it('should be committed if not rolled back', function(done) { - transaction.createRequest_ = function(method) { - assert.equal(method, 'commit'); - done(); + it('should not mark as finalized if commit errors', function(done) { + transaction.createRequest_ = function(method, proto, respType, callback) { + callback(new Error('Error.')); }; - transaction.finalize(); + transaction.commit(function() { + assert.strictEqual(transaction.isFinalized, false); + done(); + }); }); }); - describe('save', function() { - var key = new Key({ - namespace: null, - path: ['Kind', 1] - }); - - it('should not set an indexed value by default', function() { - transaction.createRequest_ = function(method, req) { - var property = req.mutation.upsert[0].property[0]; - assert.strictEqual(property.value.indexed, null); + describe('finalize', function() { + it('should be committed if not finalized', function(done) { + transaction.isFinalized = false; + transaction.commit = function () { + done(); }; - transaction.save({ - key: key, - data: [{ name: 'name', value: 'value' }] - }, assert.ifError); + transaction.finalize(); }); - it('should allow setting the indexed value of a property', function() { - transaction.createRequest_ = function(method, req) { - var property = req.mutation.upsert[0].property[0]; - assert.equal(property.name, 'name'); - assert.equal(property.value.string_value, 'value'); - assert.strictEqual(property.value.indexed, false); - }; - transaction.save({ - key: key, - data: [{ name: 'name', value: 'value', excludeFromIndexes: true }] - }, assert.ifError); + it('should execute callback if already finalized', function(done) { + transaction.isFinalized = true; + transaction.finalize(done); }); }); });