1007 lines
33 KiB
Python
1007 lines
33 KiB
Python
|
# 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.
|
||
|
|
||
|
import unittest2
|
||
|
|
||
|
|
||
|
def _make_entity_pb(project, kind, integer_id, name=None, str_val=None):
|
||
|
from gcloud.datastore._generated import entity_pb2
|
||
|
from gcloud.datastore.helpers import _new_value_pb
|
||
|
|
||
|
entity_pb = entity_pb2.Entity()
|
||
|
entity_pb.key.partition_id.project_id = project
|
||
|
path_element = entity_pb.key.path.add()
|
||
|
path_element.kind = kind
|
||
|
path_element.id = integer_id
|
||
|
if name is not None and str_val is not None:
|
||
|
value_pb = _new_value_pb(entity_pb, name)
|
||
|
value_pb.string_value = str_val
|
||
|
|
||
|
return entity_pb
|
||
|
|
||
|
|
||
|
class Test__get_gcd_project(unittest2.TestCase):
|
||
|
|
||
|
def _callFUT(self):
|
||
|
from gcloud.datastore.client import _get_gcd_project
|
||
|
return _get_gcd_project()
|
||
|
|
||
|
def test_no_value(self):
|
||
|
import os
|
||
|
from gcloud._testing import _Monkey
|
||
|
|
||
|
environ = {}
|
||
|
with _Monkey(os, getenv=environ.get):
|
||
|
project = self._callFUT()
|
||
|
self.assertEqual(project, None)
|
||
|
|
||
|
def test_value_set(self):
|
||
|
import os
|
||
|
from gcloud._testing import _Monkey
|
||
|
from gcloud.datastore.client import GCD_DATASET
|
||
|
|
||
|
MOCK_PROJECT = object()
|
||
|
environ = {GCD_DATASET: MOCK_PROJECT}
|
||
|
with _Monkey(os, getenv=environ.get):
|
||
|
project = self._callFUT()
|
||
|
self.assertEqual(project, MOCK_PROJECT)
|
||
|
|
||
|
|
||
|
class Test__determine_default_project(unittest2.TestCase):
|
||
|
|
||
|
def _callFUT(self, project=None):
|
||
|
from gcloud.datastore.client import (
|
||
|
_determine_default_project)
|
||
|
return _determine_default_project(project=project)
|
||
|
|
||
|
def _determine_default_helper(self, gcd=None, fallback=None,
|
||
|
project_called=None):
|
||
|
from gcloud._testing import _Monkey
|
||
|
from gcloud.datastore import client
|
||
|
|
||
|
_callers = []
|
||
|
|
||
|
def gcd_mock():
|
||
|
_callers.append('gcd_mock')
|
||
|
return gcd
|
||
|
|
||
|
def fallback_mock(project=None):
|
||
|
_callers.append(('fallback_mock', project))
|
||
|
return fallback
|
||
|
|
||
|
patched_methods = {
|
||
|
'_get_gcd_project': gcd_mock,
|
||
|
'_base_default_project': fallback_mock,
|
||
|
}
|
||
|
|
||
|
with _Monkey(client, **patched_methods):
|
||
|
returned_project = self._callFUT(project_called)
|
||
|
|
||
|
return returned_project, _callers
|
||
|
|
||
|
def test_no_value(self):
|
||
|
project, callers = self._determine_default_helper()
|
||
|
self.assertEqual(project, None)
|
||
|
self.assertEqual(callers, ['gcd_mock', ('fallback_mock', None)])
|
||
|
|
||
|
def test_explicit(self):
|
||
|
PROJECT = object()
|
||
|
project, callers = self._determine_default_helper(
|
||
|
project_called=PROJECT)
|
||
|
self.assertEqual(project, PROJECT)
|
||
|
self.assertEqual(callers, [])
|
||
|
|
||
|
def test_gcd(self):
|
||
|
PROJECT = object()
|
||
|
project, callers = self._determine_default_helper(gcd=PROJECT)
|
||
|
self.assertEqual(project, PROJECT)
|
||
|
self.assertEqual(callers, ['gcd_mock'])
|
||
|
|
||
|
def test_fallback(self):
|
||
|
PROJECT = object()
|
||
|
project, callers = self._determine_default_helper(fallback=PROJECT)
|
||
|
self.assertEqual(project, PROJECT)
|
||
|
self.assertEqual(callers, ['gcd_mock', ('fallback_mock', None)])
|
||
|
|
||
|
|
||
|
class TestClient(unittest2.TestCase):
|
||
|
|
||
|
PROJECT = 'PROJECT'
|
||
|
|
||
|
def setUp(self):
|
||
|
KLASS = self._getTargetClass()
|
||
|
self.original_cnxn_class = KLASS._connection_class
|
||
|
KLASS._connection_class = _MockConnection
|
||
|
|
||
|
def tearDown(self):
|
||
|
KLASS = self._getTargetClass()
|
||
|
KLASS._connection_class = self.original_cnxn_class
|
||
|
|
||
|
def _getTargetClass(self):
|
||
|
from gcloud.datastore.client import Client
|
||
|
return Client
|
||
|
|
||
|
def _makeOne(self, project=PROJECT, namespace=None,
|
||
|
credentials=None, http=None):
|
||
|
return self._getTargetClass()(project=project,
|
||
|
namespace=namespace,
|
||
|
credentials=credentials,
|
||
|
http=http)
|
||
|
|
||
|
def test_ctor_w_project_no_environ(self):
|
||
|
from gcloud._testing import _Monkey
|
||
|
from gcloud.datastore import client as _MUT
|
||
|
|
||
|
# Some environments (e.g. AppVeyor CI) run in GCE, so
|
||
|
# this test would fail artificially.
|
||
|
with _Monkey(_MUT, _base_default_project=lambda project: None):
|
||
|
self.assertRaises(EnvironmentError, self._makeOne, None)
|
||
|
|
||
|
def test_ctor_w_implicit_inputs(self):
|
||
|
from gcloud._testing import _Monkey
|
||
|
from gcloud.datastore import client as _MUT
|
||
|
from gcloud import client as _base_client
|
||
|
|
||
|
OTHER = 'other'
|
||
|
creds = object()
|
||
|
default_called = []
|
||
|
|
||
|
def fallback_mock(project):
|
||
|
default_called.append(project)
|
||
|
return project or OTHER
|
||
|
|
||
|
klass = self._getTargetClass()
|
||
|
with _Monkey(_MUT,
|
||
|
_determine_default_project=fallback_mock):
|
||
|
with _Monkey(_base_client,
|
||
|
get_credentials=lambda: creds):
|
||
|
client = klass()
|
||
|
self.assertEqual(client.project, OTHER)
|
||
|
self.assertEqual(client.namespace, None)
|
||
|
self.assertTrue(isinstance(client.connection, _MockConnection))
|
||
|
self.assertTrue(client.connection.credentials is creds)
|
||
|
self.assertTrue(client.connection.http is None)
|
||
|
self.assertTrue(client.current_batch is None)
|
||
|
self.assertTrue(client.current_transaction is None)
|
||
|
self.assertEqual(default_called, [None])
|
||
|
|
||
|
def test_ctor_w_explicit_inputs(self):
|
||
|
OTHER = 'other'
|
||
|
NAMESPACE = 'namespace'
|
||
|
creds = object()
|
||
|
http = object()
|
||
|
client = self._makeOne(project=OTHER,
|
||
|
namespace=NAMESPACE,
|
||
|
credentials=creds,
|
||
|
http=http)
|
||
|
self.assertEqual(client.project, OTHER)
|
||
|
self.assertEqual(client.namespace, NAMESPACE)
|
||
|
self.assertTrue(isinstance(client.connection, _MockConnection))
|
||
|
self.assertTrue(client.connection.credentials is creds)
|
||
|
self.assertTrue(client.connection.http is http)
|
||
|
self.assertTrue(client.current_batch is None)
|
||
|
self.assertEqual(list(client._batch_stack), [])
|
||
|
|
||
|
def test__push_batch_and__pop_batch(self):
|
||
|
creds = object()
|
||
|
client = self._makeOne(credentials=creds)
|
||
|
batch = client.batch()
|
||
|
xact = client.transaction()
|
||
|
client._push_batch(batch)
|
||
|
self.assertEqual(list(client._batch_stack), [batch])
|
||
|
self.assertTrue(client.current_batch is batch)
|
||
|
self.assertTrue(client.current_transaction is None)
|
||
|
client._push_batch(xact)
|
||
|
self.assertTrue(client.current_batch is xact)
|
||
|
self.assertTrue(client.current_transaction is xact)
|
||
|
# list(_LocalStack) returns in reverse order.
|
||
|
self.assertEqual(list(client._batch_stack), [xact, batch])
|
||
|
self.assertTrue(client._pop_batch() is xact)
|
||
|
self.assertEqual(list(client._batch_stack), [batch])
|
||
|
self.assertTrue(client._pop_batch() is batch)
|
||
|
self.assertEqual(list(client._batch_stack), [])
|
||
|
|
||
|
def test_get_miss(self):
|
||
|
_called_with = []
|
||
|
|
||
|
def _get_multi(*args, **kw):
|
||
|
_called_with.append((args, kw))
|
||
|
return []
|
||
|
|
||
|
creds = object()
|
||
|
client = self._makeOne(credentials=creds)
|
||
|
client.get_multi = _get_multi
|
||
|
|
||
|
key = object()
|
||
|
|
||
|
self.assertTrue(client.get(key) is None)
|
||
|
|
||
|
self.assertEqual(_called_with[0][0], ())
|
||
|
self.assertEqual(_called_with[0][1]['keys'], [key])
|
||
|
self.assertTrue(_called_with[0][1]['missing'] is None)
|
||
|
self.assertTrue(_called_with[0][1]['deferred'] is None)
|
||
|
self.assertTrue(_called_with[0][1]['transaction'] is None)
|
||
|
|
||
|
def test_get_hit(self):
|
||
|
TXN_ID = '123'
|
||
|
_called_with = []
|
||
|
_entity = object()
|
||
|
|
||
|
def _get_multi(*args, **kw):
|
||
|
_called_with.append((args, kw))
|
||
|
return [_entity]
|
||
|
|
||
|
creds = object()
|
||
|
client = self._makeOne(credentials=creds)
|
||
|
client.get_multi = _get_multi
|
||
|
|
||
|
key, missing, deferred = object(), [], []
|
||
|
|
||
|
self.assertTrue(client.get(key, missing, deferred, TXN_ID) is _entity)
|
||
|
|
||
|
self.assertEqual(_called_with[0][0], ())
|
||
|
self.assertEqual(_called_with[0][1]['keys'], [key])
|
||
|
self.assertTrue(_called_with[0][1]['missing'] is missing)
|
||
|
self.assertTrue(_called_with[0][1]['deferred'] is deferred)
|
||
|
self.assertEqual(_called_with[0][1]['transaction'], TXN_ID)
|
||
|
|
||
|
def test_get_multi_no_keys(self):
|
||
|
creds = object()
|
||
|
client = self._makeOne(credentials=creds)
|
||
|
results = client.get_multi([])
|
||
|
self.assertEqual(results, [])
|
||
|
|
||
|
def test_get_multi_miss(self):
|
||
|
from gcloud.datastore.key import Key
|
||
|
|
||
|
creds = object()
|
||
|
client = self._makeOne(credentials=creds)
|
||
|
client.connection._add_lookup_result()
|
||
|
key = Key('Kind', 1234, project=self.PROJECT)
|
||
|
results = client.get_multi([key])
|
||
|
self.assertEqual(results, [])
|
||
|
|
||
|
def test_get_multi_miss_w_missing(self):
|
||
|
from gcloud.datastore._generated import entity_pb2
|
||
|
from gcloud.datastore.key import Key
|
||
|
|
||
|
KIND = 'Kind'
|
||
|
ID = 1234
|
||
|
|
||
|
# Make a missing entity pb to be returned from mock backend.
|
||
|
missed = entity_pb2.Entity()
|
||
|
missed.key.partition_id.project_id = self.PROJECT
|
||
|
path_element = missed.key.path.add()
|
||
|
path_element.kind = KIND
|
||
|
path_element.id = ID
|
||
|
|
||
|
creds = object()
|
||
|
client = self._makeOne(credentials=creds)
|
||
|
# Set missing entity on mock connection.
|
||
|
client.connection._add_lookup_result(missing=[missed])
|
||
|
|
||
|
key = Key(KIND, ID, project=self.PROJECT)
|
||
|
missing = []
|
||
|
entities = client.get_multi([key], missing=missing)
|
||
|
self.assertEqual(entities, [])
|
||
|
self.assertEqual([missed.key.to_protobuf() for missed in missing],
|
||
|
[key.to_protobuf()])
|
||
|
|
||
|
def test_get_multi_w_missing_non_empty(self):
|
||
|
from gcloud.datastore.key import Key
|
||
|
|
||
|
creds = object()
|
||
|
client = self._makeOne(credentials=creds)
|
||
|
key = Key('Kind', 1234, project=self.PROJECT)
|
||
|
|
||
|
missing = ['this', 'list', 'is', 'not', 'empty']
|
||
|
self.assertRaises(ValueError, client.get_multi,
|
||
|
[key], missing=missing)
|
||
|
|
||
|
def test_get_multi_w_deferred_non_empty(self):
|
||
|
from gcloud.datastore.key import Key
|
||
|
|
||
|
creds = object()
|
||
|
client = self._makeOne(credentials=creds)
|
||
|
key = Key('Kind', 1234, project=self.PROJECT)
|
||
|
|
||
|
deferred = ['this', 'list', 'is', 'not', 'empty']
|
||
|
self.assertRaises(ValueError, client.get_multi,
|
||
|
[key], deferred=deferred)
|
||
|
|
||
|
def test_get_multi_miss_w_deferred(self):
|
||
|
from gcloud.datastore.key import Key
|
||
|
|
||
|
key = Key('Kind', 1234, project=self.PROJECT)
|
||
|
|
||
|
# Set deferred entity on mock connection.
|
||
|
creds = object()
|
||
|
client = self._makeOne(credentials=creds)
|
||
|
client.connection._add_lookup_result(deferred=[key.to_protobuf()])
|
||
|
|
||
|
deferred = []
|
||
|
entities = client.get_multi([key], deferred=deferred)
|
||
|
self.assertEqual(entities, [])
|
||
|
self.assertEqual([def_key.to_protobuf() for def_key in deferred],
|
||
|
[key.to_protobuf()])
|
||
|
|
||
|
def test_get_multi_w_deferred_from_backend_but_not_passed(self):
|
||
|
from gcloud.datastore._generated import entity_pb2
|
||
|
from gcloud.datastore.entity import Entity
|
||
|
from gcloud.datastore.key import Key
|
||
|
|
||
|
key1 = Key('Kind', project=self.PROJECT)
|
||
|
key1_pb = key1.to_protobuf()
|
||
|
key2 = Key('Kind', 2345, project=self.PROJECT)
|
||
|
key2_pb = key2.to_protobuf()
|
||
|
|
||
|
entity1_pb = entity_pb2.Entity()
|
||
|
entity1_pb.key.CopyFrom(key1_pb)
|
||
|
entity2_pb = entity_pb2.Entity()
|
||
|
entity2_pb.key.CopyFrom(key2_pb)
|
||
|
|
||
|
creds = object()
|
||
|
client = self._makeOne(credentials=creds)
|
||
|
# mock up two separate requests
|
||
|
client.connection._add_lookup_result([entity1_pb], deferred=[key2_pb])
|
||
|
client.connection._add_lookup_result([entity2_pb])
|
||
|
|
||
|
missing = []
|
||
|
found = client.get_multi([key1, key2], missing=missing)
|
||
|
self.assertEqual(len(found), 2)
|
||
|
self.assertEqual(len(missing), 0)
|
||
|
|
||
|
# Check the actual contents on the response.
|
||
|
self.assertTrue(isinstance(found[0], Entity))
|
||
|
self.assertEqual(found[0].key.path, key1.path)
|
||
|
self.assertEqual(found[0].key.project, key1.project)
|
||
|
|
||
|
self.assertTrue(isinstance(found[1], Entity))
|
||
|
self.assertEqual(found[1].key.path, key2.path)
|
||
|
self.assertEqual(found[1].key.project, key2.project)
|
||
|
|
||
|
cw = client.connection._lookup_cw
|
||
|
self.assertEqual(len(cw), 2)
|
||
|
|
||
|
ds_id, k_pbs, eventual, tid = cw[0]
|
||
|
self.assertEqual(ds_id, self.PROJECT)
|
||
|
self.assertEqual(len(k_pbs), 2)
|
||
|
self.assertEqual(key1_pb, k_pbs[0])
|
||
|
self.assertEqual(key2_pb, k_pbs[1])
|
||
|
self.assertFalse(eventual)
|
||
|
self.assertTrue(tid is None)
|
||
|
|
||
|
ds_id, k_pbs, eventual, tid = cw[1]
|
||
|
self.assertEqual(ds_id, self.PROJECT)
|
||
|
self.assertEqual(len(k_pbs), 1)
|
||
|
self.assertEqual(key2_pb, k_pbs[0])
|
||
|
self.assertFalse(eventual)
|
||
|
self.assertTrue(tid is None)
|
||
|
|
||
|
def test_get_multi_hit(self):
|
||
|
from gcloud.datastore.key import Key
|
||
|
|
||
|
KIND = 'Kind'
|
||
|
ID = 1234
|
||
|
PATH = [{'kind': KIND, 'id': ID}]
|
||
|
|
||
|
# Make a found entity pb to be returned from mock backend.
|
||
|
entity_pb = _make_entity_pb(self.PROJECT, KIND, ID, 'foo', 'Foo')
|
||
|
|
||
|
# Make a connection to return the entity pb.
|
||
|
creds = object()
|
||
|
client = self._makeOne(credentials=creds)
|
||
|
client.connection._add_lookup_result([entity_pb])
|
||
|
|
||
|
key = Key(KIND, ID, project=self.PROJECT)
|
||
|
result, = client.get_multi([key])
|
||
|
new_key = result.key
|
||
|
|
||
|
# Check the returned value is as expected.
|
||
|
self.assertFalse(new_key is key)
|
||
|
self.assertEqual(new_key.project, self.PROJECT)
|
||
|
self.assertEqual(new_key.path, PATH)
|
||
|
self.assertEqual(list(result), ['foo'])
|
||
|
self.assertEqual(result['foo'], 'Foo')
|
||
|
|
||
|
def test_get_multi_hit_w_transaction(self):
|
||
|
from gcloud.datastore.key import Key
|
||
|
|
||
|
TXN_ID = '123'
|
||
|
KIND = 'Kind'
|
||
|
ID = 1234
|
||
|
PATH = [{'kind': KIND, 'id': ID}]
|
||
|
|
||
|
# Make a found entity pb to be returned from mock backend.
|
||
|
entity_pb = _make_entity_pb(self.PROJECT, KIND, ID, 'foo', 'Foo')
|
||
|
|
||
|
# Make a connection to return the entity pb.
|
||
|
creds = object()
|
||
|
client = self._makeOne(credentials=creds)
|
||
|
client.connection._add_lookup_result([entity_pb])
|
||
|
|
||
|
key = Key(KIND, ID, project=self.PROJECT)
|
||
|
txn = client.transaction()
|
||
|
txn._id = TXN_ID
|
||
|
result, = client.get_multi([key], transaction=txn)
|
||
|
new_key = result.key
|
||
|
|
||
|
# Check the returned value is as expected.
|
||
|
self.assertFalse(new_key is key)
|
||
|
self.assertEqual(new_key.project, self.PROJECT)
|
||
|
self.assertEqual(new_key.path, PATH)
|
||
|
self.assertEqual(list(result), ['foo'])
|
||
|
self.assertEqual(result['foo'], 'Foo')
|
||
|
|
||
|
cw = client.connection._lookup_cw
|
||
|
self.assertEqual(len(cw), 1)
|
||
|
_, _, _, transaction_id = cw[0]
|
||
|
self.assertEqual(transaction_id, TXN_ID)
|
||
|
|
||
|
def test_get_multi_hit_multiple_keys_same_project(self):
|
||
|
from gcloud.datastore.key import Key
|
||
|
|
||
|
KIND = 'Kind'
|
||
|
ID1 = 1234
|
||
|
ID2 = 2345
|
||
|
|
||
|
# Make a found entity pb to be returned from mock backend.
|
||
|
entity_pb1 = _make_entity_pb(self.PROJECT, KIND, ID1)
|
||
|
entity_pb2 = _make_entity_pb(self.PROJECT, KIND, ID2)
|
||
|
|
||
|
# Make a connection to return the entity pbs.
|
||
|
creds = object()
|
||
|
client = self._makeOne(credentials=creds)
|
||
|
client.connection._add_lookup_result([entity_pb1, entity_pb2])
|
||
|
|
||
|
key1 = Key(KIND, ID1, project=self.PROJECT)
|
||
|
key2 = Key(KIND, ID2, project=self.PROJECT)
|
||
|
retrieved1, retrieved2 = client.get_multi([key1, key2])
|
||
|
|
||
|
# Check values match.
|
||
|
self.assertEqual(retrieved1.key.path, key1.path)
|
||
|
self.assertEqual(dict(retrieved1), {})
|
||
|
self.assertEqual(retrieved2.key.path, key2.path)
|
||
|
self.assertEqual(dict(retrieved2), {})
|
||
|
|
||
|
def test_get_multi_hit_multiple_keys_different_project(self):
|
||
|
from gcloud.datastore.key import Key
|
||
|
|
||
|
PROJECT1 = 'PROJECT'
|
||
|
PROJECT2 = 'PROJECT-ALT'
|
||
|
|
||
|
# Make sure our IDs are actually different.
|
||
|
self.assertNotEqual(PROJECT1, PROJECT2)
|
||
|
|
||
|
key1 = Key('KIND', 1234, project=PROJECT1)
|
||
|
key2 = Key('KIND', 1234, project=PROJECT2)
|
||
|
|
||
|
creds = object()
|
||
|
client = self._makeOne(credentials=creds)
|
||
|
|
||
|
with self.assertRaises(ValueError):
|
||
|
client.get_multi([key1, key2])
|
||
|
|
||
|
def test_get_multi_max_loops(self):
|
||
|
from gcloud._testing import _Monkey
|
||
|
from gcloud.datastore import client as _MUT
|
||
|
from gcloud.datastore.key import Key
|
||
|
|
||
|
KIND = 'Kind'
|
||
|
ID = 1234
|
||
|
|
||
|
# Make a found entity pb to be returned from mock backend.
|
||
|
entity_pb = _make_entity_pb(self.PROJECT, KIND, ID, 'foo', 'Foo')
|
||
|
|
||
|
# Make a connection to return the entity pb.
|
||
|
creds = object()
|
||
|
client = self._makeOne(credentials=creds)
|
||
|
client.connection._add_lookup_result([entity_pb])
|
||
|
|
||
|
key = Key(KIND, ID, project=self.PROJECT)
|
||
|
deferred = []
|
||
|
missing = []
|
||
|
with _Monkey(_MUT, _MAX_LOOPS=-1):
|
||
|
result = client.get_multi([key], missing=missing,
|
||
|
deferred=deferred)
|
||
|
|
||
|
# Make sure we have no results, even though the connection has been
|
||
|
# set up as in `test_hit` to return a single result.
|
||
|
self.assertEqual(result, [])
|
||
|
self.assertEqual(missing, [])
|
||
|
self.assertEqual(deferred, [])
|
||
|
|
||
|
def test_put(self):
|
||
|
_called_with = []
|
||
|
|
||
|
def _put_multi(*args, **kw):
|
||
|
_called_with.append((args, kw))
|
||
|
|
||
|
creds = object()
|
||
|
client = self._makeOne(credentials=creds)
|
||
|
client.put_multi = _put_multi
|
||
|
entity = object()
|
||
|
|
||
|
client.put(entity)
|
||
|
|
||
|
self.assertEqual(_called_with[0][0], ())
|
||
|
self.assertEqual(_called_with[0][1]['entities'], [entity])
|
||
|
|
||
|
def test_put_multi_no_entities(self):
|
||
|
creds = object()
|
||
|
client = self._makeOne(credentials=creds)
|
||
|
self.assertEqual(client.put_multi([]), None)
|
||
|
|
||
|
def test_put_multi_w_single_empty_entity(self):
|
||
|
# https://github.com/GoogleCloudPlatform/gcloud-python/issues/649
|
||
|
from gcloud.datastore.entity import Entity
|
||
|
|
||
|
creds = object()
|
||
|
client = self._makeOne(credentials=creds)
|
||
|
self.assertRaises(ValueError, client.put_multi, Entity())
|
||
|
|
||
|
def test_put_multi_no_batch_w_partial_key(self):
|
||
|
from gcloud.datastore.helpers import _property_tuples
|
||
|
from gcloud.datastore.test_batch import _Entity
|
||
|
from gcloud.datastore.test_batch import _Key
|
||
|
from gcloud.datastore.test_batch import _KeyPB
|
||
|
from gcloud.datastore.test_batch import _mutated_pb
|
||
|
|
||
|
entity = _Entity(foo=u'bar')
|
||
|
key = entity.key = _Key(self.PROJECT)
|
||
|
key._id = None
|
||
|
|
||
|
creds = object()
|
||
|
client = self._makeOne(credentials=creds)
|
||
|
client.connection._commit.append([_KeyPB(key)])
|
||
|
|
||
|
result = client.put_multi([entity])
|
||
|
self.assertTrue(result is None)
|
||
|
|
||
|
self.assertEqual(len(client.connection._commit_cw), 1)
|
||
|
(project,
|
||
|
commit_req, transaction_id) = client.connection._commit_cw[0]
|
||
|
self.assertEqual(project, self.PROJECT)
|
||
|
|
||
|
mutated_entity = _mutated_pb(self, commit_req.mutations, 'insert')
|
||
|
self.assertEqual(mutated_entity.key, key.to_protobuf())
|
||
|
|
||
|
prop_list = list(_property_tuples(mutated_entity))
|
||
|
self.assertTrue(len(prop_list), 1)
|
||
|
name, value_pb = prop_list[0]
|
||
|
self.assertEqual(name, 'foo')
|
||
|
self.assertEqual(value_pb.string_value, u'bar')
|
||
|
|
||
|
self.assertTrue(transaction_id is None)
|
||
|
|
||
|
def test_put_multi_existing_batch_w_completed_key(self):
|
||
|
from gcloud.datastore.helpers import _property_tuples
|
||
|
from gcloud.datastore.test_batch import _Entity
|
||
|
from gcloud.datastore.test_batch import _Key
|
||
|
from gcloud.datastore.test_batch import _mutated_pb
|
||
|
|
||
|
creds = object()
|
||
|
client = self._makeOne(credentials=creds)
|
||
|
entity = _Entity(foo=u'bar')
|
||
|
key = entity.key = _Key(self.PROJECT)
|
||
|
|
||
|
with _NoCommitBatch(client) as CURR_BATCH:
|
||
|
result = client.put_multi([entity])
|
||
|
|
||
|
self.assertEqual(result, None)
|
||
|
mutated_entity = _mutated_pb(self, CURR_BATCH.mutations, 'upsert')
|
||
|
self.assertEqual(mutated_entity.key, key.to_protobuf())
|
||
|
|
||
|
prop_list = list(_property_tuples(mutated_entity))
|
||
|
self.assertTrue(len(prop_list), 1)
|
||
|
name, value_pb = prop_list[0]
|
||
|
self.assertEqual(name, 'foo')
|
||
|
self.assertEqual(value_pb.string_value, u'bar')
|
||
|
|
||
|
def test_delete(self):
|
||
|
_called_with = []
|
||
|
|
||
|
def _delete_multi(*args, **kw):
|
||
|
_called_with.append((args, kw))
|
||
|
|
||
|
creds = object()
|
||
|
client = self._makeOne(credentials=creds)
|
||
|
client.delete_multi = _delete_multi
|
||
|
key = object()
|
||
|
|
||
|
client.delete(key)
|
||
|
|
||
|
self.assertEqual(_called_with[0][0], ())
|
||
|
self.assertEqual(_called_with[0][1]['keys'], [key])
|
||
|
|
||
|
def test_delete_multi_no_keys(self):
|
||
|
creds = object()
|
||
|
client = self._makeOne(credentials=creds)
|
||
|
result = client.delete_multi([])
|
||
|
self.assertEqual(result, None)
|
||
|
self.assertEqual(len(client.connection._commit_cw), 0)
|
||
|
|
||
|
def test_delete_multi_no_batch(self):
|
||
|
from gcloud.datastore.test_batch import _Key
|
||
|
from gcloud.datastore.test_batch import _mutated_pb
|
||
|
|
||
|
key = _Key(self.PROJECT)
|
||
|
|
||
|
creds = object()
|
||
|
client = self._makeOne(credentials=creds)
|
||
|
client.connection._commit.append([])
|
||
|
|
||
|
result = client.delete_multi([key])
|
||
|
self.assertEqual(result, None)
|
||
|
self.assertEqual(len(client.connection._commit_cw), 1)
|
||
|
(project,
|
||
|
commit_req, transaction_id) = client.connection._commit_cw[0]
|
||
|
self.assertEqual(project, self.PROJECT)
|
||
|
|
||
|
mutated_key = _mutated_pb(self, commit_req.mutations, 'delete')
|
||
|
self.assertEqual(mutated_key, key.to_protobuf())
|
||
|
self.assertTrue(transaction_id is None)
|
||
|
|
||
|
def test_delete_multi_w_existing_batch(self):
|
||
|
from gcloud.datastore.test_batch import _Key
|
||
|
from gcloud.datastore.test_batch import _mutated_pb
|
||
|
|
||
|
creds = object()
|
||
|
client = self._makeOne(credentials=creds)
|
||
|
key = _Key(self.PROJECT)
|
||
|
|
||
|
with _NoCommitBatch(client) as CURR_BATCH:
|
||
|
result = client.delete_multi([key])
|
||
|
|
||
|
self.assertEqual(result, None)
|
||
|
mutated_key = _mutated_pb(self, CURR_BATCH.mutations, 'delete')
|
||
|
self.assertEqual(mutated_key, key._key)
|
||
|
self.assertEqual(len(client.connection._commit_cw), 0)
|
||
|
|
||
|
def test_delete_multi_w_existing_transaction(self):
|
||
|
from gcloud.datastore.test_batch import _Key
|
||
|
from gcloud.datastore.test_batch import _mutated_pb
|
||
|
|
||
|
creds = object()
|
||
|
client = self._makeOne(credentials=creds)
|
||
|
key = _Key(self.PROJECT)
|
||
|
|
||
|
with _NoCommitTransaction(client) as CURR_XACT:
|
||
|
result = client.delete_multi([key])
|
||
|
|
||
|
self.assertEqual(result, None)
|
||
|
mutated_key = _mutated_pb(self, CURR_XACT.mutations, 'delete')
|
||
|
self.assertEqual(mutated_key, key._key)
|
||
|
self.assertEqual(len(client.connection._commit_cw), 0)
|
||
|
|
||
|
def test_allocate_ids_w_partial_key(self):
|
||
|
from gcloud.datastore.test_batch import _Key
|
||
|
|
||
|
NUM_IDS = 2
|
||
|
|
||
|
INCOMPLETE_KEY = _Key(self.PROJECT)
|
||
|
INCOMPLETE_KEY._id = None
|
||
|
|
||
|
creds = object()
|
||
|
client = self._makeOne(credentials=creds)
|
||
|
|
||
|
result = client.allocate_ids(INCOMPLETE_KEY, NUM_IDS)
|
||
|
|
||
|
# Check the IDs returned.
|
||
|
self.assertEqual([key._id for key in result], list(range(NUM_IDS)))
|
||
|
|
||
|
def test_allocate_ids_with_completed_key(self):
|
||
|
from gcloud.datastore.test_batch import _Key
|
||
|
|
||
|
creds = object()
|
||
|
client = self._makeOne(credentials=creds)
|
||
|
|
||
|
COMPLETE_KEY = _Key(self.PROJECT)
|
||
|
self.assertRaises(ValueError, client.allocate_ids, COMPLETE_KEY, 2)
|
||
|
|
||
|
def test_key_w_project(self):
|
||
|
KIND = 'KIND'
|
||
|
ID = 1234
|
||
|
|
||
|
creds = object()
|
||
|
client = self._makeOne(credentials=creds)
|
||
|
|
||
|
self.assertRaises(TypeError,
|
||
|
client.key, KIND, ID, project=self.PROJECT)
|
||
|
|
||
|
def test_key_wo_project(self):
|
||
|
from gcloud.datastore import client as MUT
|
||
|
from gcloud._testing import _Monkey
|
||
|
|
||
|
KIND = 'KIND'
|
||
|
ID = 1234
|
||
|
|
||
|
creds = object()
|
||
|
client = self._makeOne(credentials=creds)
|
||
|
|
||
|
with _Monkey(MUT, Key=_Dummy):
|
||
|
key = client.key(KIND, ID)
|
||
|
|
||
|
self.assertTrue(isinstance(key, _Dummy))
|
||
|
self.assertEqual(key.args, (KIND, ID))
|
||
|
expected_kwargs = {
|
||
|
'project': self.PROJECT,
|
||
|
'namespace': None,
|
||
|
}
|
||
|
self.assertEqual(key.kwargs, expected_kwargs)
|
||
|
|
||
|
def test_key_w_namespace(self):
|
||
|
from gcloud.datastore import client as MUT
|
||
|
from gcloud._testing import _Monkey
|
||
|
|
||
|
KIND = 'KIND'
|
||
|
ID = 1234
|
||
|
NAMESPACE = object()
|
||
|
|
||
|
creds = object()
|
||
|
client = self._makeOne(namespace=NAMESPACE, credentials=creds)
|
||
|
|
||
|
with _Monkey(MUT, Key=_Dummy):
|
||
|
key = client.key(KIND, ID)
|
||
|
|
||
|
self.assertTrue(isinstance(key, _Dummy))
|
||
|
expected_kwargs = {
|
||
|
'project': self.PROJECT,
|
||
|
'namespace': NAMESPACE,
|
||
|
}
|
||
|
self.assertEqual(key.kwargs, expected_kwargs)
|
||
|
|
||
|
def test_key_w_namespace_collision(self):
|
||
|
from gcloud.datastore import client as MUT
|
||
|
from gcloud._testing import _Monkey
|
||
|
|
||
|
KIND = 'KIND'
|
||
|
ID = 1234
|
||
|
NAMESPACE1 = object()
|
||
|
NAMESPACE2 = object()
|
||
|
|
||
|
creds = object()
|
||
|
client = self._makeOne(namespace=NAMESPACE1, credentials=creds)
|
||
|
|
||
|
with _Monkey(MUT, Key=_Dummy):
|
||
|
key = client.key(KIND, ID, namespace=NAMESPACE2)
|
||
|
|
||
|
self.assertTrue(isinstance(key, _Dummy))
|
||
|
expected_kwargs = {
|
||
|
'project': self.PROJECT,
|
||
|
'namespace': NAMESPACE2,
|
||
|
}
|
||
|
self.assertEqual(key.kwargs, expected_kwargs)
|
||
|
|
||
|
def test_batch(self):
|
||
|
from gcloud.datastore import client as MUT
|
||
|
from gcloud._testing import _Monkey
|
||
|
|
||
|
creds = object()
|
||
|
client = self._makeOne(credentials=creds)
|
||
|
|
||
|
with _Monkey(MUT, Batch=_Dummy):
|
||
|
batch = client.batch()
|
||
|
|
||
|
self.assertTrue(isinstance(batch, _Dummy))
|
||
|
self.assertEqual(batch.args, (client,))
|
||
|
self.assertEqual(batch.kwargs, {})
|
||
|
|
||
|
def test_transaction_defaults(self):
|
||
|
from gcloud.datastore import client as MUT
|
||
|
from gcloud._testing import _Monkey
|
||
|
|
||
|
creds = object()
|
||
|
client = self._makeOne(credentials=creds)
|
||
|
|
||
|
with _Monkey(MUT, Transaction=_Dummy):
|
||
|
xact = client.transaction()
|
||
|
|
||
|
self.assertTrue(isinstance(xact, _Dummy))
|
||
|
self.assertEqual(xact.args, (client,))
|
||
|
self.assertEqual(xact.kwargs, {})
|
||
|
|
||
|
def test_query_w_client(self):
|
||
|
KIND = 'KIND'
|
||
|
|
||
|
creds = object()
|
||
|
client = self._makeOne(credentials=creds)
|
||
|
other = self._makeOne(credentials=object())
|
||
|
|
||
|
self.assertRaises(TypeError, client.query, kind=KIND, client=other)
|
||
|
|
||
|
def test_query_w_project(self):
|
||
|
KIND = 'KIND'
|
||
|
|
||
|
creds = object()
|
||
|
client = self._makeOne(credentials=creds)
|
||
|
|
||
|
self.assertRaises(TypeError,
|
||
|
client.query, kind=KIND, project=self.PROJECT)
|
||
|
|
||
|
def test_query_w_defaults(self):
|
||
|
from gcloud.datastore import client as MUT
|
||
|
from gcloud._testing import _Monkey
|
||
|
|
||
|
creds = object()
|
||
|
client = self._makeOne(credentials=creds)
|
||
|
|
||
|
with _Monkey(MUT, Query=_Dummy):
|
||
|
query = client.query()
|
||
|
|
||
|
self.assertTrue(isinstance(query, _Dummy))
|
||
|
self.assertEqual(query.args, (client,))
|
||
|
expected_kwargs = {
|
||
|
'project': self.PROJECT,
|
||
|
'namespace': None,
|
||
|
}
|
||
|
self.assertEqual(query.kwargs, expected_kwargs)
|
||
|
|
||
|
def test_query_explicit(self):
|
||
|
from gcloud.datastore import client as MUT
|
||
|
from gcloud._testing import _Monkey
|
||
|
|
||
|
KIND = 'KIND'
|
||
|
NAMESPACE = 'NAMESPACE'
|
||
|
ANCESTOR = object()
|
||
|
FILTERS = [('PROPERTY', '==', 'VALUE')]
|
||
|
PROJECTION = ['__key__']
|
||
|
ORDER = ['PROPERTY']
|
||
|
DISTINCT_ON = ['DISTINCT_ON']
|
||
|
|
||
|
creds = object()
|
||
|
client = self._makeOne(credentials=creds)
|
||
|
|
||
|
with _Monkey(MUT, Query=_Dummy):
|
||
|
query = client.query(
|
||
|
kind=KIND,
|
||
|
namespace=NAMESPACE,
|
||
|
ancestor=ANCESTOR,
|
||
|
filters=FILTERS,
|
||
|
projection=PROJECTION,
|
||
|
order=ORDER,
|
||
|
distinct_on=DISTINCT_ON,
|
||
|
)
|
||
|
|
||
|
self.assertTrue(isinstance(query, _Dummy))
|
||
|
self.assertEqual(query.args, (client,))
|
||
|
kwargs = {
|
||
|
'project': self.PROJECT,
|
||
|
'kind': KIND,
|
||
|
'namespace': NAMESPACE,
|
||
|
'ancestor': ANCESTOR,
|
||
|
'filters': FILTERS,
|
||
|
'projection': PROJECTION,
|
||
|
'order': ORDER,
|
||
|
'distinct_on': DISTINCT_ON,
|
||
|
}
|
||
|
self.assertEqual(query.kwargs, kwargs)
|
||
|
|
||
|
def test_query_w_namespace(self):
|
||
|
from gcloud.datastore import client as MUT
|
||
|
from gcloud._testing import _Monkey
|
||
|
|
||
|
KIND = 'KIND'
|
||
|
NAMESPACE = object()
|
||
|
|
||
|
creds = object()
|
||
|
client = self._makeOne(namespace=NAMESPACE, credentials=creds)
|
||
|
|
||
|
with _Monkey(MUT, Query=_Dummy):
|
||
|
query = client.query(kind=KIND)
|
||
|
|
||
|
self.assertTrue(isinstance(query, _Dummy))
|
||
|
self.assertEqual(query.args, (client,))
|
||
|
expected_kwargs = {
|
||
|
'project': self.PROJECT,
|
||
|
'namespace': NAMESPACE,
|
||
|
'kind': KIND,
|
||
|
}
|
||
|
self.assertEqual(query.kwargs, expected_kwargs)
|
||
|
|
||
|
def test_query_w_namespace_collision(self):
|
||
|
from gcloud.datastore import client as MUT
|
||
|
from gcloud._testing import _Monkey
|
||
|
|
||
|
KIND = 'KIND'
|
||
|
NAMESPACE1 = object()
|
||
|
NAMESPACE2 = object()
|
||
|
|
||
|
creds = object()
|
||
|
client = self._makeOne(namespace=NAMESPACE1, credentials=creds)
|
||
|
|
||
|
with _Monkey(MUT, Query=_Dummy):
|
||
|
query = client.query(kind=KIND, namespace=NAMESPACE2)
|
||
|
|
||
|
self.assertTrue(isinstance(query, _Dummy))
|
||
|
self.assertEqual(query.args, (client,))
|
||
|
expected_kwargs = {
|
||
|
'project': self.PROJECT,
|
||
|
'namespace': NAMESPACE2,
|
||
|
'kind': KIND,
|
||
|
}
|
||
|
self.assertEqual(query.kwargs, expected_kwargs)
|
||
|
|
||
|
|
||
|
class _Dummy(object):
|
||
|
|
||
|
def __init__(self, *args, **kwargs):
|
||
|
self.args = args
|
||
|
self.kwargs = kwargs
|
||
|
|
||
|
|
||
|
class _MockConnection(object):
|
||
|
|
||
|
def __init__(self, credentials=None, http=None):
|
||
|
self.credentials = credentials
|
||
|
self.http = http
|
||
|
self._lookup_cw = []
|
||
|
self._lookup = []
|
||
|
self._commit_cw = []
|
||
|
self._commit = []
|
||
|
self._alloc_cw = []
|
||
|
self._alloc = []
|
||
|
self._index_updates = 0
|
||
|
|
||
|
def _add_lookup_result(self, results=(), missing=(), deferred=()):
|
||
|
self._lookup.append((list(results), list(missing), list(deferred)))
|
||
|
|
||
|
def lookup(self, project, key_pbs, eventual=False, transaction_id=None):
|
||
|
self._lookup_cw.append((project, key_pbs, eventual, transaction_id))
|
||
|
triple, self._lookup = self._lookup[0], self._lookup[1:]
|
||
|
results, missing, deferred = triple
|
||
|
return results, missing, deferred
|
||
|
|
||
|
def commit(self, project, commit_request, transaction_id):
|
||
|
self._commit_cw.append((project, commit_request, transaction_id))
|
||
|
response, self._commit = self._commit[0], self._commit[1:]
|
||
|
return self._index_updates, response
|
||
|
|
||
|
def allocate_ids(self, project, key_pbs):
|
||
|
from gcloud.datastore.test_connection import _KeyProto
|
||
|
self._alloc_cw.append((project, key_pbs))
|
||
|
num_pbs = len(key_pbs)
|
||
|
return [_KeyProto(i) for i in list(range(num_pbs))]
|
||
|
|
||
|
|
||
|
class _NoCommitBatch(object):
|
||
|
|
||
|
def __init__(self, client):
|
||
|
from gcloud.datastore.batch import Batch
|
||
|
self._client = client
|
||
|
self._batch = Batch(client)
|
||
|
|
||
|
def __enter__(self):
|
||
|
self._client._push_batch(self._batch)
|
||
|
return self._batch
|
||
|
|
||
|
def __exit__(self, *args):
|
||
|
self._client._pop_batch()
|
||
|
|
||
|
|
||
|
class _NoCommitTransaction(object):
|
||
|
|
||
|
def __init__(self, client, transaction_id='TRANSACTION'):
|
||
|
from gcloud.datastore.transaction import Transaction
|
||
|
self._client = client
|
||
|
xact = self._transaction = Transaction(client)
|
||
|
xact._id = transaction_id
|
||
|
|
||
|
def __enter__(self):
|
||
|
self._client._push_batch(self._transaction)
|
||
|
return self._transaction
|
||
|
|
||
|
def __exit__(self, *args):
|
||
|
self._client._pop_batch()
|