432 lines
16 KiB
Python
432 lines
16 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
|
||
|
|
||
|
|
||
|
class TestKey(unittest2.TestCase):
|
||
|
|
||
|
_DEFAULT_PROJECT = 'PROJECT'
|
||
|
|
||
|
def _getTargetClass(self):
|
||
|
from gcloud.datastore.key import Key
|
||
|
return Key
|
||
|
|
||
|
def _makeOne(self, *args, **kwargs):
|
||
|
return self._getTargetClass()(*args, **kwargs)
|
||
|
|
||
|
def test_ctor_empty(self):
|
||
|
self.assertRaises(ValueError, self._makeOne)
|
||
|
|
||
|
def test_ctor_no_project(self):
|
||
|
klass = self._getTargetClass()
|
||
|
self.assertRaises(ValueError, klass, 'KIND')
|
||
|
|
||
|
def test_ctor_w_explicit_project_empty_path(self):
|
||
|
_PROJECT = 'PROJECT'
|
||
|
self.assertRaises(ValueError, self._makeOne, project=_PROJECT)
|
||
|
|
||
|
def test_ctor_parent(self):
|
||
|
_PARENT_KIND = 'KIND1'
|
||
|
_PARENT_ID = 1234
|
||
|
_PARENT_PROJECT = 'PROJECT-ALT'
|
||
|
_PARENT_NAMESPACE = 'NAMESPACE'
|
||
|
_CHILD_KIND = 'KIND2'
|
||
|
_CHILD_ID = 2345
|
||
|
_PATH = [
|
||
|
{'kind': _PARENT_KIND, 'id': _PARENT_ID},
|
||
|
{'kind': _CHILD_KIND, 'id': _CHILD_ID},
|
||
|
]
|
||
|
parent_key = self._makeOne(_PARENT_KIND, _PARENT_ID,
|
||
|
project=_PARENT_PROJECT,
|
||
|
namespace=_PARENT_NAMESPACE)
|
||
|
key = self._makeOne(_CHILD_KIND, _CHILD_ID, parent=parent_key)
|
||
|
self.assertEqual(key.project, parent_key.project)
|
||
|
self.assertEqual(key.namespace, parent_key.namespace)
|
||
|
self.assertEqual(key.kind, _CHILD_KIND)
|
||
|
self.assertEqual(key.path, _PATH)
|
||
|
self.assertTrue(key.parent is parent_key)
|
||
|
|
||
|
def test_ctor_partial_parent(self):
|
||
|
parent_key = self._makeOne('KIND', project=self._DEFAULT_PROJECT)
|
||
|
with self.assertRaises(ValueError):
|
||
|
self._makeOne('KIND2', 1234, parent=parent_key)
|
||
|
|
||
|
def test_ctor_parent_bad_type(self):
|
||
|
with self.assertRaises(AttributeError):
|
||
|
self._makeOne('KIND2', 1234, parent=('KIND1', 1234),
|
||
|
project=self._DEFAULT_PROJECT)
|
||
|
|
||
|
def test_ctor_parent_bad_namespace(self):
|
||
|
parent_key = self._makeOne('KIND', 1234, namespace='FOO',
|
||
|
project=self._DEFAULT_PROJECT)
|
||
|
with self.assertRaises(ValueError):
|
||
|
self._makeOne('KIND2', 1234, namespace='BAR', parent=parent_key,
|
||
|
project=self._DEFAULT_PROJECT)
|
||
|
|
||
|
def test_ctor_parent_bad_project(self):
|
||
|
parent_key = self._makeOne('KIND', 1234, project='FOO')
|
||
|
with self.assertRaises(ValueError):
|
||
|
self._makeOne('KIND2', 1234, parent=parent_key,
|
||
|
project='BAR')
|
||
|
|
||
|
def test_ctor_parent_empty_path(self):
|
||
|
parent_key = self._makeOne('KIND', 1234,
|
||
|
project=self._DEFAULT_PROJECT)
|
||
|
with self.assertRaises(ValueError):
|
||
|
self._makeOne(parent=parent_key)
|
||
|
|
||
|
def test_ctor_explicit(self):
|
||
|
_PROJECT = 'PROJECT-ALT'
|
||
|
_NAMESPACE = 'NAMESPACE'
|
||
|
_KIND = 'KIND'
|
||
|
_ID = 1234
|
||
|
_PATH = [{'kind': _KIND, 'id': _ID}]
|
||
|
key = self._makeOne(_KIND, _ID, namespace=_NAMESPACE,
|
||
|
project=_PROJECT)
|
||
|
self.assertEqual(key.project, _PROJECT)
|
||
|
self.assertEqual(key.namespace, _NAMESPACE)
|
||
|
self.assertEqual(key.kind, _KIND)
|
||
|
self.assertEqual(key.path, _PATH)
|
||
|
|
||
|
def test_ctor_bad_kind(self):
|
||
|
self.assertRaises(ValueError, self._makeOne, object(),
|
||
|
project=self._DEFAULT_PROJECT)
|
||
|
|
||
|
def test_ctor_bad_id_or_name(self):
|
||
|
self.assertRaises(ValueError, self._makeOne, 'KIND', object(),
|
||
|
project=self._DEFAULT_PROJECT)
|
||
|
self.assertRaises(ValueError, self._makeOne, 'KIND', None,
|
||
|
project=self._DEFAULT_PROJECT)
|
||
|
self.assertRaises(ValueError, self._makeOne, 'KIND', 10, 'KIND2', None,
|
||
|
project=self._DEFAULT_PROJECT)
|
||
|
|
||
|
def test__clone(self):
|
||
|
_PROJECT = 'PROJECT-ALT'
|
||
|
_NAMESPACE = 'NAMESPACE'
|
||
|
_KIND = 'KIND'
|
||
|
_ID = 1234
|
||
|
_PATH = [{'kind': _KIND, 'id': _ID}]
|
||
|
key = self._makeOne(_KIND, _ID, namespace=_NAMESPACE,
|
||
|
project=_PROJECT)
|
||
|
clone = key._clone()
|
||
|
self.assertEqual(clone.project, _PROJECT)
|
||
|
self.assertEqual(clone.namespace, _NAMESPACE)
|
||
|
self.assertEqual(clone.kind, _KIND)
|
||
|
self.assertEqual(clone.path, _PATH)
|
||
|
|
||
|
def test__clone_with_parent(self):
|
||
|
_PROJECT = 'PROJECT-ALT'
|
||
|
_NAMESPACE = 'NAMESPACE'
|
||
|
_KIND1 = 'PARENT'
|
||
|
_KIND2 = 'KIND'
|
||
|
_ID1 = 1234
|
||
|
_ID2 = 2345
|
||
|
_PATH = [{'kind': _KIND1, 'id': _ID1}, {'kind': _KIND2, 'id': _ID2}]
|
||
|
|
||
|
parent = self._makeOne(_KIND1, _ID1, namespace=_NAMESPACE,
|
||
|
project=_PROJECT)
|
||
|
key = self._makeOne(_KIND2, _ID2, parent=parent)
|
||
|
self.assertTrue(key.parent is parent)
|
||
|
clone = key._clone()
|
||
|
self.assertTrue(clone.parent is key.parent)
|
||
|
self.assertEqual(clone.project, _PROJECT)
|
||
|
self.assertEqual(clone.namespace, _NAMESPACE)
|
||
|
self.assertEqual(clone.path, _PATH)
|
||
|
|
||
|
def test___eq_____ne___w_non_key(self):
|
||
|
_PROJECT = 'PROJECT'
|
||
|
_KIND = 'KIND'
|
||
|
_NAME = 'one'
|
||
|
key = self._makeOne(_KIND, _NAME, project=_PROJECT)
|
||
|
self.assertFalse(key == object())
|
||
|
self.assertTrue(key != object())
|
||
|
|
||
|
def test___eq_____ne___two_incomplete_keys_same_kind(self):
|
||
|
_PROJECT = 'PROJECT'
|
||
|
_KIND = 'KIND'
|
||
|
key1 = self._makeOne(_KIND, project=_PROJECT)
|
||
|
key2 = self._makeOne(_KIND, project=_PROJECT)
|
||
|
self.assertFalse(key1 == key2)
|
||
|
self.assertTrue(key1 != key2)
|
||
|
|
||
|
def test___eq_____ne___incomplete_key_w_complete_key_same_kind(self):
|
||
|
_PROJECT = 'PROJECT'
|
||
|
_KIND = 'KIND'
|
||
|
_ID = 1234
|
||
|
key1 = self._makeOne(_KIND, project=_PROJECT)
|
||
|
key2 = self._makeOne(_KIND, _ID, project=_PROJECT)
|
||
|
self.assertFalse(key1 == key2)
|
||
|
self.assertTrue(key1 != key2)
|
||
|
|
||
|
def test___eq_____ne___complete_key_w_incomplete_key_same_kind(self):
|
||
|
_PROJECT = 'PROJECT'
|
||
|
_KIND = 'KIND'
|
||
|
_ID = 1234
|
||
|
key1 = self._makeOne(_KIND, _ID, project=_PROJECT)
|
||
|
key2 = self._makeOne(_KIND, project=_PROJECT)
|
||
|
self.assertFalse(key1 == key2)
|
||
|
self.assertTrue(key1 != key2)
|
||
|
|
||
|
def test___eq_____ne___same_kind_different_ids(self):
|
||
|
_PROJECT = 'PROJECT'
|
||
|
_KIND = 'KIND'
|
||
|
_ID1 = 1234
|
||
|
_ID2 = 2345
|
||
|
key1 = self._makeOne(_KIND, _ID1, project=_PROJECT)
|
||
|
key2 = self._makeOne(_KIND, _ID2, project=_PROJECT)
|
||
|
self.assertFalse(key1 == key2)
|
||
|
self.assertTrue(key1 != key2)
|
||
|
|
||
|
def test___eq_____ne___same_kind_and_id(self):
|
||
|
_PROJECT = 'PROJECT'
|
||
|
_KIND = 'KIND'
|
||
|
_ID = 1234
|
||
|
key1 = self._makeOne(_KIND, _ID, project=_PROJECT)
|
||
|
key2 = self._makeOne(_KIND, _ID, project=_PROJECT)
|
||
|
self.assertTrue(key1 == key2)
|
||
|
self.assertFalse(key1 != key2)
|
||
|
|
||
|
def test___eq_____ne___same_kind_and_id_different_project(self):
|
||
|
_PROJECT1 = 'PROJECT1'
|
||
|
_PROJECT2 = 'PROJECT2'
|
||
|
_KIND = 'KIND'
|
||
|
_ID = 1234
|
||
|
key1 = self._makeOne(_KIND, _ID, project=_PROJECT1)
|
||
|
key2 = self._makeOne(_KIND, _ID, project=_PROJECT2)
|
||
|
self.assertFalse(key1 == key2)
|
||
|
self.assertTrue(key1 != key2)
|
||
|
|
||
|
def test___eq_____ne___same_kind_and_id_different_namespace(self):
|
||
|
_PROJECT = 'PROJECT'
|
||
|
_NAMESPACE1 = 'NAMESPACE1'
|
||
|
_NAMESPACE2 = 'NAMESPACE2'
|
||
|
_KIND = 'KIND'
|
||
|
_ID = 1234
|
||
|
key1 = self._makeOne(_KIND, _ID, project=_PROJECT,
|
||
|
namespace=_NAMESPACE1)
|
||
|
key2 = self._makeOne(_KIND, _ID, project=_PROJECT,
|
||
|
namespace=_NAMESPACE2)
|
||
|
self.assertFalse(key1 == key2)
|
||
|
self.assertTrue(key1 != key2)
|
||
|
|
||
|
def test___eq_____ne___same_kind_different_names(self):
|
||
|
_PROJECT = 'PROJECT'
|
||
|
_KIND = 'KIND'
|
||
|
_NAME1 = 'one'
|
||
|
_NAME2 = 'two'
|
||
|
key1 = self._makeOne(_KIND, _NAME1, project=_PROJECT)
|
||
|
key2 = self._makeOne(_KIND, _NAME2, project=_PROJECT)
|
||
|
self.assertFalse(key1 == key2)
|
||
|
self.assertTrue(key1 != key2)
|
||
|
|
||
|
def test___eq_____ne___same_kind_and_name(self):
|
||
|
_PROJECT = 'PROJECT'
|
||
|
_KIND = 'KIND'
|
||
|
_NAME = 'one'
|
||
|
key1 = self._makeOne(_KIND, _NAME, project=_PROJECT)
|
||
|
key2 = self._makeOne(_KIND, _NAME, project=_PROJECT)
|
||
|
self.assertTrue(key1 == key2)
|
||
|
self.assertFalse(key1 != key2)
|
||
|
|
||
|
def test___eq_____ne___same_kind_and_name_different_project(self):
|
||
|
_PROJECT1 = 'PROJECT1'
|
||
|
_PROJECT2 = 'PROJECT2'
|
||
|
_KIND = 'KIND'
|
||
|
_NAME = 'one'
|
||
|
key1 = self._makeOne(_KIND, _NAME, project=_PROJECT1)
|
||
|
key2 = self._makeOne(_KIND, _NAME, project=_PROJECT2)
|
||
|
self.assertFalse(key1 == key2)
|
||
|
self.assertTrue(key1 != key2)
|
||
|
|
||
|
def test___eq_____ne___same_kind_and_name_different_namespace(self):
|
||
|
_PROJECT = 'PROJECT'
|
||
|
_NAMESPACE1 = 'NAMESPACE1'
|
||
|
_NAMESPACE2 = 'NAMESPACE2'
|
||
|
_KIND = 'KIND'
|
||
|
_NAME = 'one'
|
||
|
key1 = self._makeOne(_KIND, _NAME, project=_PROJECT,
|
||
|
namespace=_NAMESPACE1)
|
||
|
key2 = self._makeOne(_KIND, _NAME, project=_PROJECT,
|
||
|
namespace=_NAMESPACE2)
|
||
|
self.assertFalse(key1 == key2)
|
||
|
self.assertTrue(key1 != key2)
|
||
|
|
||
|
def test___hash___incomplete(self):
|
||
|
_PROJECT = 'PROJECT'
|
||
|
_KIND = 'KIND'
|
||
|
key = self._makeOne(_KIND, project=_PROJECT)
|
||
|
self.assertNotEqual(hash(key),
|
||
|
hash(_KIND) + hash(_PROJECT) + hash(None))
|
||
|
|
||
|
def test___hash___completed_w_id(self):
|
||
|
_PROJECT = 'PROJECT'
|
||
|
_KIND = 'KIND'
|
||
|
_ID = 1234
|
||
|
key = self._makeOne(_KIND, _ID, project=_PROJECT)
|
||
|
self.assertNotEqual(hash(key),
|
||
|
hash(_KIND) + hash(_ID) +
|
||
|
hash(_PROJECT) + hash(None))
|
||
|
|
||
|
def test___hash___completed_w_name(self):
|
||
|
_PROJECT = 'PROJECT'
|
||
|
_KIND = 'KIND'
|
||
|
_NAME = 'NAME'
|
||
|
key = self._makeOne(_KIND, _NAME, project=_PROJECT)
|
||
|
self.assertNotEqual(hash(key),
|
||
|
hash(_KIND) + hash(_NAME) +
|
||
|
hash(_PROJECT) + hash(None))
|
||
|
|
||
|
def test_completed_key_on_partial_w_id(self):
|
||
|
key = self._makeOne('KIND', project=self._DEFAULT_PROJECT)
|
||
|
_ID = 1234
|
||
|
new_key = key.completed_key(_ID)
|
||
|
self.assertFalse(key is new_key)
|
||
|
self.assertEqual(new_key.id, _ID)
|
||
|
self.assertEqual(new_key.name, None)
|
||
|
|
||
|
def test_completed_key_on_partial_w_name(self):
|
||
|
key = self._makeOne('KIND', project=self._DEFAULT_PROJECT)
|
||
|
_NAME = 'NAME'
|
||
|
new_key = key.completed_key(_NAME)
|
||
|
self.assertFalse(key is new_key)
|
||
|
self.assertEqual(new_key.id, None)
|
||
|
self.assertEqual(new_key.name, _NAME)
|
||
|
|
||
|
def test_completed_key_on_partial_w_invalid(self):
|
||
|
key = self._makeOne('KIND', project=self._DEFAULT_PROJECT)
|
||
|
self.assertRaises(ValueError, key.completed_key, object())
|
||
|
|
||
|
def test_completed_key_on_complete(self):
|
||
|
key = self._makeOne('KIND', 1234, project=self._DEFAULT_PROJECT)
|
||
|
self.assertRaises(ValueError, key.completed_key, 5678)
|
||
|
|
||
|
def test_to_protobuf_defaults(self):
|
||
|
from gcloud.datastore._generated import entity_pb2
|
||
|
|
||
|
_KIND = 'KIND'
|
||
|
key = self._makeOne(_KIND, project=self._DEFAULT_PROJECT)
|
||
|
pb = key.to_protobuf()
|
||
|
self.assertTrue(isinstance(pb, entity_pb2.Key))
|
||
|
|
||
|
# Check partition ID.
|
||
|
self.assertEqual(pb.partition_id.project_id, self._DEFAULT_PROJECT)
|
||
|
# Unset values are False-y.
|
||
|
self.assertEqual(pb.partition_id.namespace_id, '')
|
||
|
|
||
|
# Check the element PB matches the partial key and kind.
|
||
|
elem, = list(pb.path)
|
||
|
self.assertEqual(elem.kind, _KIND)
|
||
|
# Unset values are False-y.
|
||
|
self.assertEqual(elem.name, '')
|
||
|
# Unset values are False-y.
|
||
|
self.assertEqual(elem.id, 0)
|
||
|
|
||
|
def test_to_protobuf_w_explicit_project(self):
|
||
|
_PROJECT = 'PROJECT-ALT'
|
||
|
key = self._makeOne('KIND', project=_PROJECT)
|
||
|
pb = key.to_protobuf()
|
||
|
self.assertEqual(pb.partition_id.project_id, _PROJECT)
|
||
|
|
||
|
def test_to_protobuf_w_explicit_namespace(self):
|
||
|
_NAMESPACE = 'NAMESPACE'
|
||
|
key = self._makeOne('KIND', namespace=_NAMESPACE,
|
||
|
project=self._DEFAULT_PROJECT)
|
||
|
pb = key.to_protobuf()
|
||
|
self.assertEqual(pb.partition_id.namespace_id, _NAMESPACE)
|
||
|
|
||
|
def test_to_protobuf_w_explicit_path(self):
|
||
|
_PARENT = 'PARENT'
|
||
|
_CHILD = 'CHILD'
|
||
|
_ID = 1234
|
||
|
_NAME = 'NAME'
|
||
|
key = self._makeOne(_PARENT, _NAME, _CHILD, _ID,
|
||
|
project=self._DEFAULT_PROJECT)
|
||
|
pb = key.to_protobuf()
|
||
|
elems = list(pb.path)
|
||
|
self.assertEqual(len(elems), 2)
|
||
|
self.assertEqual(elems[0].kind, _PARENT)
|
||
|
self.assertEqual(elems[0].name, _NAME)
|
||
|
self.assertEqual(elems[1].kind, _CHILD)
|
||
|
self.assertEqual(elems[1].id, _ID)
|
||
|
|
||
|
def test_to_protobuf_w_no_kind(self):
|
||
|
key = self._makeOne('KIND', project=self._DEFAULT_PROJECT)
|
||
|
# Force the 'kind' to be unset. Maybe `to_protobuf` should fail
|
||
|
# on this? The backend certainly will.
|
||
|
key._path[-1].pop('kind')
|
||
|
pb = key.to_protobuf()
|
||
|
# Unset values are False-y.
|
||
|
self.assertEqual(pb.path[0].kind, '')
|
||
|
|
||
|
def test_is_partial_no_name_or_id(self):
|
||
|
key = self._makeOne('KIND', project=self._DEFAULT_PROJECT)
|
||
|
self.assertTrue(key.is_partial)
|
||
|
|
||
|
def test_is_partial_w_id(self):
|
||
|
_ID = 1234
|
||
|
key = self._makeOne('KIND', _ID, project=self._DEFAULT_PROJECT)
|
||
|
self.assertFalse(key.is_partial)
|
||
|
|
||
|
def test_is_partial_w_name(self):
|
||
|
_NAME = 'NAME'
|
||
|
key = self._makeOne('KIND', _NAME, project=self._DEFAULT_PROJECT)
|
||
|
self.assertFalse(key.is_partial)
|
||
|
|
||
|
def test_id_or_name_no_name_or_id(self):
|
||
|
key = self._makeOne('KIND', project=self._DEFAULT_PROJECT)
|
||
|
self.assertEqual(key.id_or_name, None)
|
||
|
|
||
|
def test_id_or_name_no_name_or_id_child(self):
|
||
|
key = self._makeOne('KIND1', 1234, 'KIND2',
|
||
|
project=self._DEFAULT_PROJECT)
|
||
|
self.assertEqual(key.id_or_name, None)
|
||
|
|
||
|
def test_id_or_name_w_id_only(self):
|
||
|
_ID = 1234
|
||
|
key = self._makeOne('KIND', _ID, project=self._DEFAULT_PROJECT)
|
||
|
self.assertEqual(key.id_or_name, _ID)
|
||
|
|
||
|
def test_id_or_name_w_name_only(self):
|
||
|
_NAME = 'NAME'
|
||
|
key = self._makeOne('KIND', _NAME, project=self._DEFAULT_PROJECT)
|
||
|
self.assertEqual(key.id_or_name, _NAME)
|
||
|
|
||
|
def test_parent_default(self):
|
||
|
key = self._makeOne('KIND', project=self._DEFAULT_PROJECT)
|
||
|
self.assertEqual(key.parent, None)
|
||
|
|
||
|
def test_parent_explicit_top_level(self):
|
||
|
key = self._makeOne('KIND', 1234, project=self._DEFAULT_PROJECT)
|
||
|
self.assertEqual(key.parent, None)
|
||
|
|
||
|
def test_parent_explicit_nested(self):
|
||
|
_PARENT_KIND = 'KIND1'
|
||
|
_PARENT_ID = 1234
|
||
|
_PARENT_PATH = [{'kind': _PARENT_KIND, 'id': _PARENT_ID}]
|
||
|
key = self._makeOne(_PARENT_KIND, _PARENT_ID, 'KIND2',
|
||
|
project=self._DEFAULT_PROJECT)
|
||
|
self.assertEqual(key.parent.path, _PARENT_PATH)
|
||
|
|
||
|
def test_parent_multiple_calls(self):
|
||
|
_PARENT_KIND = 'KIND1'
|
||
|
_PARENT_ID = 1234
|
||
|
_PARENT_PATH = [{'kind': _PARENT_KIND, 'id': _PARENT_ID}]
|
||
|
key = self._makeOne(_PARENT_KIND, _PARENT_ID, 'KIND2',
|
||
|
project=self._DEFAULT_PROJECT)
|
||
|
parent = key.parent
|
||
|
self.assertEqual(parent.path, _PARENT_PATH)
|
||
|
new_parent = key.parent
|
||
|
self.assertTrue(parent is new_parent)
|