Index: tools/telemetry/third_party/gsutil/third_party/boto/tests/unit/dynamodb2/test_table.py |
diff --git a/tools/telemetry/third_party/gsutil/third_party/boto/tests/unit/dynamodb2/test_table.py b/tools/telemetry/third_party/gsutil/third_party/boto/tests/unit/dynamodb2/test_table.py |
deleted file mode 100644 |
index 87bdbe48f8ec0f8c4200a30d890bb112e390e984..0000000000000000000000000000000000000000 |
--- a/tools/telemetry/third_party/gsutil/third_party/boto/tests/unit/dynamodb2/test_table.py |
+++ /dev/null |
@@ -1,3066 +0,0 @@ |
-from tests.compat import mock, unittest |
-from boto.dynamodb2 import exceptions |
-from boto.dynamodb2.fields import (HashKey, RangeKey, |
- AllIndex, KeysOnlyIndex, IncludeIndex, |
- GlobalAllIndex, GlobalKeysOnlyIndex, |
- GlobalIncludeIndex) |
-from boto.dynamodb2.items import Item |
-from boto.dynamodb2.layer1 import DynamoDBConnection |
-from boto.dynamodb2.results import ResultSet, BatchGetResultSet |
-from boto.dynamodb2.table import Table |
-from boto.dynamodb2.types import (STRING, NUMBER, BINARY, |
- FILTER_OPERATORS, QUERY_OPERATORS) |
-from boto.exception import JSONResponseError |
-from boto.compat import six, long_type |
- |
- |
-FakeDynamoDBConnection = mock.create_autospec(DynamoDBConnection) |
- |
- |
-class SchemaFieldsTestCase(unittest.TestCase): |
- def test_hash_key(self): |
- hash_key = HashKey('hello') |
- self.assertEqual(hash_key.name, 'hello') |
- self.assertEqual(hash_key.data_type, STRING) |
- self.assertEqual(hash_key.attr_type, 'HASH') |
- |
- self.assertEqual(hash_key.definition(), { |
- 'AttributeName': 'hello', |
- 'AttributeType': 'S' |
- }) |
- self.assertEqual(hash_key.schema(), { |
- 'AttributeName': 'hello', |
- 'KeyType': 'HASH' |
- }) |
- |
- def test_range_key(self): |
- range_key = RangeKey('hello') |
- self.assertEqual(range_key.name, 'hello') |
- self.assertEqual(range_key.data_type, STRING) |
- self.assertEqual(range_key.attr_type, 'RANGE') |
- |
- self.assertEqual(range_key.definition(), { |
- 'AttributeName': 'hello', |
- 'AttributeType': 'S' |
- }) |
- self.assertEqual(range_key.schema(), { |
- 'AttributeName': 'hello', |
- 'KeyType': 'RANGE' |
- }) |
- |
- def test_alternate_type(self): |
- alt_key = HashKey('alt', data_type=NUMBER) |
- self.assertEqual(alt_key.name, 'alt') |
- self.assertEqual(alt_key.data_type, NUMBER) |
- self.assertEqual(alt_key.attr_type, 'HASH') |
- |
- self.assertEqual(alt_key.definition(), { |
- 'AttributeName': 'alt', |
- 'AttributeType': 'N' |
- }) |
- self.assertEqual(alt_key.schema(), { |
- 'AttributeName': 'alt', |
- 'KeyType': 'HASH' |
- }) |
- |
- |
-class IndexFieldTestCase(unittest.TestCase): |
- def test_all_index(self): |
- all_index = AllIndex('AllKeys', parts=[ |
- HashKey('username'), |
- RangeKey('date_joined') |
- ]) |
- self.assertEqual(all_index.name, 'AllKeys') |
- self.assertEqual([part.attr_type for part in all_index.parts], [ |
- 'HASH', |
- 'RANGE' |
- ]) |
- self.assertEqual(all_index.projection_type, 'ALL') |
- |
- self.assertEqual(all_index.definition(), [ |
- {'AttributeName': 'username', 'AttributeType': 'S'}, |
- {'AttributeName': 'date_joined', 'AttributeType': 'S'} |
- ]) |
- self.assertEqual(all_index.schema(), { |
- 'IndexName': 'AllKeys', |
- 'KeySchema': [ |
- { |
- 'AttributeName': 'username', |
- 'KeyType': 'HASH' |
- }, |
- { |
- 'AttributeName': 'date_joined', |
- 'KeyType': 'RANGE' |
- } |
- ], |
- 'Projection': { |
- 'ProjectionType': 'ALL' |
- } |
- }) |
- |
- def test_keys_only_index(self): |
- keys_only = KeysOnlyIndex('KeysOnly', parts=[ |
- HashKey('username'), |
- RangeKey('date_joined') |
- ]) |
- self.assertEqual(keys_only.name, 'KeysOnly') |
- self.assertEqual([part.attr_type for part in keys_only.parts], [ |
- 'HASH', |
- 'RANGE' |
- ]) |
- self.assertEqual(keys_only.projection_type, 'KEYS_ONLY') |
- |
- self.assertEqual(keys_only.definition(), [ |
- {'AttributeName': 'username', 'AttributeType': 'S'}, |
- {'AttributeName': 'date_joined', 'AttributeType': 'S'} |
- ]) |
- self.assertEqual(keys_only.schema(), { |
- 'IndexName': 'KeysOnly', |
- 'KeySchema': [ |
- { |
- 'AttributeName': 'username', |
- 'KeyType': 'HASH' |
- }, |
- { |
- 'AttributeName': 'date_joined', |
- 'KeyType': 'RANGE' |
- } |
- ], |
- 'Projection': { |
- 'ProjectionType': 'KEYS_ONLY' |
- } |
- }) |
- |
- def test_include_index(self): |
- include_index = IncludeIndex('IncludeKeys', parts=[ |
- HashKey('username'), |
- RangeKey('date_joined') |
- ], includes=[ |
- 'gender', |
- 'friend_count' |
- ]) |
- self.assertEqual(include_index.name, 'IncludeKeys') |
- self.assertEqual([part.attr_type for part in include_index.parts], [ |
- 'HASH', |
- 'RANGE' |
- ]) |
- self.assertEqual(include_index.projection_type, 'INCLUDE') |
- |
- self.assertEqual(include_index.definition(), [ |
- {'AttributeName': 'username', 'AttributeType': 'S'}, |
- {'AttributeName': 'date_joined', 'AttributeType': 'S'} |
- ]) |
- self.assertEqual(include_index.schema(), { |
- 'IndexName': 'IncludeKeys', |
- 'KeySchema': [ |
- { |
- 'AttributeName': 'username', |
- 'KeyType': 'HASH' |
- }, |
- { |
- 'AttributeName': 'date_joined', |
- 'KeyType': 'RANGE' |
- } |
- ], |
- 'Projection': { |
- 'ProjectionType': 'INCLUDE', |
- 'NonKeyAttributes': [ |
- 'gender', |
- 'friend_count', |
- ] |
- } |
- }) |
- |
- def test_global_all_index(self): |
- all_index = GlobalAllIndex('AllKeys', parts=[ |
- HashKey('username'), |
- RangeKey('date_joined') |
- ], |
- throughput={ |
- 'read': 6, |
- 'write': 2, |
- }) |
- self.assertEqual(all_index.name, 'AllKeys') |
- self.assertEqual([part.attr_type for part in all_index.parts], [ |
- 'HASH', |
- 'RANGE' |
- ]) |
- self.assertEqual(all_index.projection_type, 'ALL') |
- |
- self.assertEqual(all_index.definition(), [ |
- {'AttributeName': 'username', 'AttributeType': 'S'}, |
- {'AttributeName': 'date_joined', 'AttributeType': 'S'} |
- ]) |
- self.assertEqual(all_index.schema(), { |
- 'IndexName': 'AllKeys', |
- 'KeySchema': [ |
- { |
- 'AttributeName': 'username', |
- 'KeyType': 'HASH' |
- }, |
- { |
- 'AttributeName': 'date_joined', |
- 'KeyType': 'RANGE' |
- } |
- ], |
- 'Projection': { |
- 'ProjectionType': 'ALL' |
- }, |
- 'ProvisionedThroughput': { |
- 'ReadCapacityUnits': 6, |
- 'WriteCapacityUnits': 2 |
- } |
- }) |
- |
- def test_global_keys_only_index(self): |
- keys_only = GlobalKeysOnlyIndex('KeysOnly', parts=[ |
- HashKey('username'), |
- RangeKey('date_joined') |
- ], |
- throughput={ |
- 'read': 3, |
- 'write': 4, |
- }) |
- self.assertEqual(keys_only.name, 'KeysOnly') |
- self.assertEqual([part.attr_type for part in keys_only.parts], [ |
- 'HASH', |
- 'RANGE' |
- ]) |
- self.assertEqual(keys_only.projection_type, 'KEYS_ONLY') |
- |
- self.assertEqual(keys_only.definition(), [ |
- {'AttributeName': 'username', 'AttributeType': 'S'}, |
- {'AttributeName': 'date_joined', 'AttributeType': 'S'} |
- ]) |
- self.assertEqual(keys_only.schema(), { |
- 'IndexName': 'KeysOnly', |
- 'KeySchema': [ |
- { |
- 'AttributeName': 'username', |
- 'KeyType': 'HASH' |
- }, |
- { |
- 'AttributeName': 'date_joined', |
- 'KeyType': 'RANGE' |
- } |
- ], |
- 'Projection': { |
- 'ProjectionType': 'KEYS_ONLY' |
- }, |
- 'ProvisionedThroughput': { |
- 'ReadCapacityUnits': 3, |
- 'WriteCapacityUnits': 4 |
- } |
- }) |
- |
- def test_global_include_index(self): |
- # Lean on the default throughput |
- include_index = GlobalIncludeIndex('IncludeKeys', parts=[ |
- HashKey('username'), |
- RangeKey('date_joined') |
- ], includes=[ |
- 'gender', |
- 'friend_count' |
- ]) |
- self.assertEqual(include_index.name, 'IncludeKeys') |
- self.assertEqual([part.attr_type for part in include_index.parts], [ |
- 'HASH', |
- 'RANGE' |
- ]) |
- self.assertEqual(include_index.projection_type, 'INCLUDE') |
- |
- self.assertEqual(include_index.definition(), [ |
- {'AttributeName': 'username', 'AttributeType': 'S'}, |
- {'AttributeName': 'date_joined', 'AttributeType': 'S'} |
- ]) |
- self.assertEqual(include_index.schema(), { |
- 'IndexName': 'IncludeKeys', |
- 'KeySchema': [ |
- { |
- 'AttributeName': 'username', |
- 'KeyType': 'HASH' |
- }, |
- { |
- 'AttributeName': 'date_joined', |
- 'KeyType': 'RANGE' |
- } |
- ], |
- 'Projection': { |
- 'ProjectionType': 'INCLUDE', |
- 'NonKeyAttributes': [ |
- 'gender', |
- 'friend_count', |
- ] |
- }, |
- 'ProvisionedThroughput': { |
- 'ReadCapacityUnits': 5, |
- 'WriteCapacityUnits': 5 |
- } |
- }) |
- |
- def test_global_include_index_throughput(self): |
- include_index = GlobalIncludeIndex('IncludeKeys', parts=[ |
- HashKey('username'), |
- RangeKey('date_joined') |
- ], includes=[ |
- 'gender', |
- 'friend_count' |
- ], throughput={ |
- 'read': 10, |
- 'write': 8 |
- }) |
- |
- self.assertEqual(include_index.schema(), { |
- 'IndexName': 'IncludeKeys', |
- 'KeySchema': [ |
- { |
- 'AttributeName': 'username', |
- 'KeyType': 'HASH' |
- }, |
- { |
- 'AttributeName': 'date_joined', |
- 'KeyType': 'RANGE' |
- } |
- ], |
- 'Projection': { |
- 'ProjectionType': 'INCLUDE', |
- 'NonKeyAttributes': [ |
- 'gender', |
- 'friend_count', |
- ] |
- }, |
- 'ProvisionedThroughput': { |
- 'ReadCapacityUnits': 10, |
- 'WriteCapacityUnits': 8 |
- } |
- }) |
- |
- |
-class ItemTestCase(unittest.TestCase): |
- if six.PY2: |
- assertCountEqual = unittest.TestCase.assertItemsEqual |
- |
- def setUp(self): |
- super(ItemTestCase, self).setUp() |
- self.table = Table('whatever', connection=FakeDynamoDBConnection()) |
- self.johndoe = self.create_item({ |
- 'username': 'johndoe', |
- 'first_name': 'John', |
- 'date_joined': 12345, |
- }) |
- |
- def create_item(self, data): |
- return Item(self.table, data=data) |
- |
- def test_initialization(self): |
- empty_item = Item(self.table) |
- self.assertEqual(empty_item.table, self.table) |
- self.assertEqual(empty_item._data, {}) |
- |
- full_item = Item(self.table, data={ |
- 'username': 'johndoe', |
- 'date_joined': 12345, |
- }) |
- self.assertEqual(full_item.table, self.table) |
- self.assertEqual(full_item._data, { |
- 'username': 'johndoe', |
- 'date_joined': 12345, |
- }) |
- |
- # The next couple methods make use of ``sorted(...)`` so we get consistent |
- # ordering everywhere & no erroneous failures. |
- |
- def test_keys(self): |
- self.assertCountEqual(self.johndoe.keys(), [ |
- 'date_joined', |
- 'first_name', |
- 'username', |
- ]) |
- |
- def test_values(self): |
- self.assertCountEqual(self.johndoe.values(), |
- [12345, 'John', 'johndoe']) |
- |
- def test_contains(self): |
- self.assertIn('username', self.johndoe) |
- self.assertIn('first_name', self.johndoe) |
- self.assertIn('date_joined', self.johndoe) |
- self.assertNotIn('whatever', self.johndoe) |
- |
- def test_iter(self): |
- self.assertCountEqual(self.johndoe, |
- ['johndoe', 'John', 12345]) |
- |
- def test_get(self): |
- self.assertEqual(self.johndoe.get('username'), 'johndoe') |
- self.assertEqual(self.johndoe.get('first_name'), 'John') |
- self.assertEqual(self.johndoe.get('date_joined'), 12345) |
- |
- # Test a missing key. No default yields ``None``. |
- self.assertEqual(self.johndoe.get('last_name'), None) |
- # This time with a default. |
- self.assertEqual(self.johndoe.get('last_name', True), True) |
- |
- def test_items(self): |
- self.assertCountEqual( |
- self.johndoe.items(), |
- [ |
- ('date_joined', 12345), |
- ('first_name', 'John'), |
- ('username', 'johndoe'), |
- ]) |
- |
- def test_attribute_access(self): |
- self.assertEqual(self.johndoe['username'], 'johndoe') |
- self.assertEqual(self.johndoe['first_name'], 'John') |
- self.assertEqual(self.johndoe['date_joined'], 12345) |
- |
- # Test a missing key. |
- self.assertEqual(self.johndoe['last_name'], None) |
- |
- # Set a key. |
- self.johndoe['last_name'] = 'Doe' |
- # Test accessing the new key. |
- self.assertEqual(self.johndoe['last_name'], 'Doe') |
- |
- # Delete a key. |
- del self.johndoe['last_name'] |
- # Test the now-missing-again key. |
- self.assertEqual(self.johndoe['last_name'], None) |
- |
- def test_needs_save(self): |
- self.johndoe.mark_clean() |
- self.assertFalse(self.johndoe.needs_save()) |
- self.johndoe['last_name'] = 'Doe' |
- self.assertTrue(self.johndoe.needs_save()) |
- |
- def test_needs_save_set_changed(self): |
- # First, ensure we're clean. |
- self.johndoe.mark_clean() |
- self.assertFalse(self.johndoe.needs_save()) |
- # Add a friends collection. |
- self.johndoe['friends'] = set(['jane', 'alice']) |
- self.assertTrue(self.johndoe.needs_save()) |
- # Now mark it clean, then change the collection. |
- # This does NOT call ``__setitem__``, so the item used to be |
- # incorrectly appearing to be clean, when it had in fact been changed. |
- self.johndoe.mark_clean() |
- self.assertFalse(self.johndoe.needs_save()) |
- self.johndoe['friends'].add('bob') |
- self.assertTrue(self.johndoe.needs_save()) |
- |
- def test_mark_clean(self): |
- self.johndoe['last_name'] = 'Doe' |
- self.assertTrue(self.johndoe.needs_save()) |
- self.johndoe.mark_clean() |
- self.assertFalse(self.johndoe.needs_save()) |
- |
- def test_load(self): |
- empty_item = Item(self.table) |
- empty_item.load({ |
- 'Item': { |
- 'username': {'S': 'johndoe'}, |
- 'first_name': {'S': 'John'}, |
- 'last_name': {'S': 'Doe'}, |
- 'date_joined': {'N': '1366056668'}, |
- 'friend_count': {'N': '3'}, |
- 'friends': {'SS': ['alice', 'bob', 'jane']}, |
- } |
- }) |
- self.assertEqual(empty_item['username'], 'johndoe') |
- self.assertEqual(empty_item['date_joined'], 1366056668) |
- self.assertEqual(sorted(empty_item['friends']), sorted([ |
- 'alice', |
- 'bob', |
- 'jane' |
- ])) |
- |
- def test_get_keys(self): |
- # Setup the data. |
- self.table.schema = [ |
- HashKey('username'), |
- RangeKey('date_joined'), |
- ] |
- self.assertEqual(self.johndoe.get_keys(), { |
- 'username': 'johndoe', |
- 'date_joined': 12345, |
- }) |
- |
- def test_get_raw_keys(self): |
- # Setup the data. |
- self.table.schema = [ |
- HashKey('username'), |
- RangeKey('date_joined'), |
- ] |
- self.assertEqual(self.johndoe.get_raw_keys(), { |
- 'username': {'S': 'johndoe'}, |
- 'date_joined': {'N': '12345'}, |
- }) |
- |
- def test_build_expects(self): |
- # Pristine. |
- self.assertEqual(self.johndoe.build_expects(), { |
- 'first_name': { |
- 'Exists': False, |
- }, |
- 'username': { |
- 'Exists': False, |
- }, |
- 'date_joined': { |
- 'Exists': False, |
- }, |
- }) |
- |
- # Without modifications. |
- self.johndoe.mark_clean() |
- self.assertEqual(self.johndoe.build_expects(), { |
- 'first_name': { |
- 'Exists': True, |
- 'Value': { |
- 'S': 'John', |
- }, |
- }, |
- 'username': { |
- 'Exists': True, |
- 'Value': { |
- 'S': 'johndoe', |
- }, |
- }, |
- 'date_joined': { |
- 'Exists': True, |
- 'Value': { |
- 'N': '12345', |
- }, |
- }, |
- }) |
- |
- # Change some data. |
- self.johndoe['first_name'] = 'Johann' |
- # Add some data. |
- self.johndoe['last_name'] = 'Doe' |
- # Delete some data. |
- del self.johndoe['date_joined'] |
- |
- # All fields (default). |
- self.assertEqual(self.johndoe.build_expects(), { |
- 'first_name': { |
- 'Exists': True, |
- 'Value': { |
- 'S': 'John', |
- }, |
- }, |
- 'last_name': { |
- 'Exists': False, |
- }, |
- 'username': { |
- 'Exists': True, |
- 'Value': { |
- 'S': 'johndoe', |
- }, |
- }, |
- 'date_joined': { |
- 'Exists': True, |
- 'Value': { |
- 'N': '12345', |
- }, |
- }, |
- }) |
- |
- # Only a subset of the fields. |
- self.assertEqual(self.johndoe.build_expects(fields=[ |
- 'first_name', |
- 'last_name', |
- 'date_joined', |
- ]), { |
- 'first_name': { |
- 'Exists': True, |
- 'Value': { |
- 'S': 'John', |
- }, |
- }, |
- 'last_name': { |
- 'Exists': False, |
- }, |
- 'date_joined': { |
- 'Exists': True, |
- 'Value': { |
- 'N': '12345', |
- }, |
- }, |
- }) |
- |
- def test_prepare_full(self): |
- self.assertEqual(self.johndoe.prepare_full(), { |
- 'username': {'S': 'johndoe'}, |
- 'first_name': {'S': 'John'}, |
- 'date_joined': {'N': '12345'} |
- }) |
- |
- self.johndoe['friends'] = set(['jane', 'alice']) |
- data = self.johndoe.prepare_full() |
- self.assertEqual(data['username'], {'S': 'johndoe'}) |
- self.assertEqual(data['first_name'], {'S': 'John'}) |
- self.assertEqual(data['date_joined'], {'N': '12345'}) |
- self.assertCountEqual(data['friends']['SS'], |
- ['jane', 'alice']) |
- |
- def test_prepare_full_empty_set(self): |
- self.johndoe['friends'] = set() |
- self.assertEqual(self.johndoe.prepare_full(), { |
- 'username': {'S': 'johndoe'}, |
- 'first_name': {'S': 'John'}, |
- 'date_joined': {'N': '12345'} |
- }) |
- |
- def test_prepare_partial(self): |
- self.johndoe.mark_clean() |
- # Change some data. |
- self.johndoe['first_name'] = 'Johann' |
- # Add some data. |
- self.johndoe['last_name'] = 'Doe' |
- # Delete some data. |
- del self.johndoe['date_joined'] |
- |
- final_data, fields = self.johndoe.prepare_partial() |
- self.assertEqual(final_data, { |
- 'date_joined': { |
- 'Action': 'DELETE', |
- }, |
- 'first_name': { |
- 'Action': 'PUT', |
- 'Value': {'S': 'Johann'}, |
- }, |
- 'last_name': { |
- 'Action': 'PUT', |
- 'Value': {'S': 'Doe'}, |
- }, |
- }) |
- self.assertEqual(fields, set([ |
- 'first_name', |
- 'last_name', |
- 'date_joined' |
- ])) |
- |
- def test_prepare_partial_empty_set(self): |
- self.johndoe.mark_clean() |
- # Change some data. |
- self.johndoe['first_name'] = 'Johann' |
- # Add some data. |
- self.johndoe['last_name'] = 'Doe' |
- # Delete some data. |
- del self.johndoe['date_joined'] |
- # Put an empty set on the ``Item``. |
- self.johndoe['friends'] = set() |
- |
- final_data, fields = self.johndoe.prepare_partial() |
- self.assertEqual(final_data, { |
- 'date_joined': { |
- 'Action': 'DELETE', |
- }, |
- 'first_name': { |
- 'Action': 'PUT', |
- 'Value': {'S': 'Johann'}, |
- }, |
- 'last_name': { |
- 'Action': 'PUT', |
- 'Value': {'S': 'Doe'}, |
- }, |
- }) |
- self.assertEqual(fields, set([ |
- 'first_name', |
- 'last_name', |
- 'date_joined' |
- ])) |
- |
- def test_save_no_changes(self): |
- # Unchanged, no save. |
- with mock.patch.object(self.table, '_put_item', return_value=True) \ |
- as mock_put_item: |
- # Pretend we loaded it via ``get_item``... |
- self.johndoe.mark_clean() |
- self.assertFalse(self.johndoe.save()) |
- |
- self.assertFalse(mock_put_item.called) |
- |
- def test_save_with_changes(self): |
- # With changed data. |
- with mock.patch.object(self.table, '_put_item', return_value=True) \ |
- as mock_put_item: |
- self.johndoe.mark_clean() |
- self.johndoe['first_name'] = 'J' |
- self.johndoe['new_attr'] = 'never_seen_before' |
- self.assertTrue(self.johndoe.save()) |
- self.assertFalse(self.johndoe.needs_save()) |
- |
- self.assertTrue(mock_put_item.called) |
- mock_put_item.assert_called_once_with({ |
- 'username': {'S': 'johndoe'}, |
- 'first_name': {'S': 'J'}, |
- 'new_attr': {'S': 'never_seen_before'}, |
- 'date_joined': {'N': '12345'} |
- }, expects={ |
- 'username': { |
- 'Value': { |
- 'S': 'johndoe', |
- }, |
- 'Exists': True, |
- }, |
- 'first_name': { |
- 'Value': { |
- 'S': 'John', |
- }, |
- 'Exists': True, |
- }, |
- 'new_attr': { |
- 'Exists': False, |
- }, |
- 'date_joined': { |
- 'Value': { |
- 'N': '12345', |
- }, |
- 'Exists': True, |
- }, |
- }) |
- |
- def test_save_with_changes_overwrite(self): |
- # With changed data. |
- with mock.patch.object(self.table, '_put_item', return_value=True) \ |
- as mock_put_item: |
- self.johndoe['first_name'] = 'J' |
- self.johndoe['new_attr'] = 'never_seen_before' |
- # OVERWRITE ALL THE THINGS |
- self.assertTrue(self.johndoe.save(overwrite=True)) |
- self.assertFalse(self.johndoe.needs_save()) |
- |
- self.assertTrue(mock_put_item.called) |
- mock_put_item.assert_called_once_with({ |
- 'username': {'S': 'johndoe'}, |
- 'first_name': {'S': 'J'}, |
- 'new_attr': {'S': 'never_seen_before'}, |
- 'date_joined': {'N': '12345'} |
- }, expects=None) |
- |
- def test_partial_no_changes(self): |
- # Unchanged, no save. |
- with mock.patch.object(self.table, '_update_item', return_value=True) \ |
- as mock_update_item: |
- # Pretend we loaded it via ``get_item``... |
- self.johndoe.mark_clean() |
- self.assertFalse(self.johndoe.partial_save()) |
- |
- self.assertFalse(mock_update_item.called) |
- |
- def test_partial_with_changes(self): |
- # Setup the data. |
- self.table.schema = [ |
- HashKey('username'), |
- ] |
- |
- # With changed data. |
- with mock.patch.object(self.table, '_update_item', return_value=True) \ |
- as mock_update_item: |
- # Pretend we loaded it via ``get_item``... |
- self.johndoe.mark_clean() |
- # Now... MODIFY!!! |
- self.johndoe['first_name'] = 'J' |
- self.johndoe['last_name'] = 'Doe' |
- del self.johndoe['date_joined'] |
- self.assertTrue(self.johndoe.partial_save()) |
- self.assertFalse(self.johndoe.needs_save()) |
- |
- self.assertTrue(mock_update_item.called) |
- mock_update_item.assert_called_once_with({ |
- 'username': 'johndoe', |
- }, { |
- 'first_name': { |
- 'Action': 'PUT', |
- 'Value': {'S': 'J'}, |
- }, |
- 'last_name': { |
- 'Action': 'PUT', |
- 'Value': {'S': 'Doe'}, |
- }, |
- 'date_joined': { |
- 'Action': 'DELETE', |
- } |
- }, expects={ |
- 'first_name': { |
- 'Value': { |
- 'S': 'John', |
- }, |
- 'Exists': True |
- }, |
- 'last_name': { |
- 'Exists': False |
- }, |
- 'date_joined': { |
- 'Value': { |
- 'N': '12345', |
- }, |
- 'Exists': True |
- }, |
- }) |
- |
- def test_delete(self): |
- # Setup the data. |
- self.table.schema = [ |
- HashKey('username'), |
- RangeKey('date_joined'), |
- ] |
- |
- with mock.patch.object(self.table, 'delete_item', return_value=True) \ |
- as mock_delete_item: |
- self.johndoe.delete() |
- |
- self.assertTrue(mock_delete_item.called) |
- mock_delete_item.assert_called_once_with( |
- username='johndoe', |
- date_joined=12345 |
- ) |
- |
- def test_nonzero(self): |
- self.assertTrue(self.johndoe) |
- self.assertFalse(self.create_item({})) |
- |
- |
-class ItemFromItemTestCase(ItemTestCase): |
- def setUp(self): |
- super(ItemFromItemTestCase, self).setUp() |
- self.johndoe = self.create_item(self.johndoe) |
- |
- |
-def fake_results(name, greeting='hello', exclusive_start_key=None, limit=None): |
- if exclusive_start_key is None: |
- exclusive_start_key = -1 |
- |
- if limit == 0: |
- raise Exception("Web Service Returns '400 Bad Request'") |
- |
- end_cap = 13 |
- results = [] |
- start_key = exclusive_start_key + 1 |
- |
- for i in range(start_key, start_key + 5): |
- if i < end_cap: |
- results.append("%s %s #%s" % (greeting, name, i)) |
- |
- # Don't return more than limit results |
- if limit < len(results): |
- results = results[:limit] |
- |
- retval = { |
- 'results': results, |
- } |
- |
- if exclusive_start_key + 5 < end_cap: |
- retval['last_key'] = exclusive_start_key + 5 |
- |
- return retval |
- |
- |
-class ResultSetTestCase(unittest.TestCase): |
- def setUp(self): |
- super(ResultSetTestCase, self).setUp() |
- self.results = ResultSet() |
- self.result_function = mock.MagicMock(side_effect=fake_results) |
- self.results.to_call(self.result_function, 'john', greeting='Hello', limit=20) |
- |
- def test_first_key(self): |
- self.assertEqual(self.results.first_key, 'exclusive_start_key') |
- |
- def test_max_page_size_fetch_more(self): |
- self.results = ResultSet(max_page_size=10) |
- self.results.to_call(self.result_function, 'john', greeting='Hello') |
- self.results.fetch_more() |
- self.result_function.assert_called_with('john', greeting='Hello', limit=10) |
- self.result_function.reset_mock() |
- |
- def test_max_page_size_and_smaller_limit_fetch_more(self): |
- self.results = ResultSet(max_page_size=10) |
- self.results.to_call(self.result_function, 'john', greeting='Hello', limit=5) |
- self.results.fetch_more() |
- self.result_function.assert_called_with('john', greeting='Hello', limit=5) |
- self.result_function.reset_mock() |
- |
- def test_max_page_size_and_bigger_limit_fetch_more(self): |
- self.results = ResultSet(max_page_size=10) |
- self.results.to_call(self.result_function, 'john', greeting='Hello', limit=15) |
- self.results.fetch_more() |
- self.result_function.assert_called_with('john', greeting='Hello', limit=10) |
- self.result_function.reset_mock() |
- |
- def test_fetch_more(self): |
- # First "page". |
- self.results.fetch_more() |
- self.assertEqual(self.results._results, [ |
- 'Hello john #0', |
- 'Hello john #1', |
- 'Hello john #2', |
- 'Hello john #3', |
- 'Hello john #4', |
- ]) |
- |
- self.result_function.assert_called_with('john', greeting='Hello', limit=20) |
- self.result_function.reset_mock() |
- |
- # Fake in a last key. |
- self.results._last_key_seen = 4 |
- # Second "page". |
- self.results.fetch_more() |
- self.assertEqual(self.results._results, [ |
- 'Hello john #5', |
- 'Hello john #6', |
- 'Hello john #7', |
- 'Hello john #8', |
- 'Hello john #9', |
- ]) |
- |
- self.result_function.assert_called_with('john', greeting='Hello', limit=20, exclusive_start_key=4) |
- self.result_function.reset_mock() |
- |
- # Fake in a last key. |
- self.results._last_key_seen = 9 |
- # Last "page". |
- self.results.fetch_more() |
- self.assertEqual(self.results._results, [ |
- 'Hello john #10', |
- 'Hello john #11', |
- 'Hello john #12', |
- ]) |
- |
- # Fake in a key outside the range. |
- self.results._last_key_seen = 15 |
- # Empty "page". Nothing new gets added |
- self.results.fetch_more() |
- self.assertEqual(self.results._results, []) |
- |
- # Make sure we won't check for results in the future. |
- self.assertFalse(self.results._results_left) |
- |
- def test_iteration(self): |
- # First page. |
- self.assertEqual(next(self.results), 'Hello john #0') |
- self.assertEqual(next(self.results), 'Hello john #1') |
- self.assertEqual(next(self.results), 'Hello john #2') |
- self.assertEqual(next(self.results), 'Hello john #3') |
- self.assertEqual(next(self.results), 'Hello john #4') |
- self.assertEqual(self.results._limit, 15) |
- # Second page. |
- self.assertEqual(next(self.results), 'Hello john #5') |
- self.assertEqual(next(self.results), 'Hello john #6') |
- self.assertEqual(next(self.results), 'Hello john #7') |
- self.assertEqual(next(self.results), 'Hello john #8') |
- self.assertEqual(next(self.results), 'Hello john #9') |
- self.assertEqual(self.results._limit, 10) |
- # Third page. |
- self.assertEqual(next(self.results), 'Hello john #10') |
- self.assertEqual(next(self.results), 'Hello john #11') |
- self.assertEqual(next(self.results), 'Hello john #12') |
- self.assertRaises(StopIteration, self.results.next) |
- self.assertEqual(self.results._limit, 7) |
- |
- def test_limit_smaller_than_first_page(self): |
- results = ResultSet() |
- results.to_call(fake_results, 'john', greeting='Hello', limit=2) |
- self.assertEqual(next(results), 'Hello john #0') |
- self.assertEqual(next(results), 'Hello john #1') |
- self.assertRaises(StopIteration, results.next) |
- |
- def test_limit_equals_page(self): |
- results = ResultSet() |
- results.to_call(fake_results, 'john', greeting='Hello', limit=5) |
- # First page |
- self.assertEqual(next(results), 'Hello john #0') |
- self.assertEqual(next(results), 'Hello john #1') |
- self.assertEqual(next(results), 'Hello john #2') |
- self.assertEqual(next(results), 'Hello john #3') |
- self.assertEqual(next(results), 'Hello john #4') |
- self.assertRaises(StopIteration, results.next) |
- |
- def test_limit_greater_than_page(self): |
- results = ResultSet() |
- results.to_call(fake_results, 'john', greeting='Hello', limit=6) |
- # First page |
- self.assertEqual(next(results), 'Hello john #0') |
- self.assertEqual(next(results), 'Hello john #1') |
- self.assertEqual(next(results), 'Hello john #2') |
- self.assertEqual(next(results), 'Hello john #3') |
- self.assertEqual(next(results), 'Hello john #4') |
- # Second page |
- self.assertEqual(next(results), 'Hello john #5') |
- self.assertRaises(StopIteration, results.next) |
- |
- def test_iteration_noresults(self): |
- def none(limit=10): |
- return { |
- 'results': [], |
- } |
- |
- results = ResultSet() |
- results.to_call(none, limit=20) |
- self.assertRaises(StopIteration, results.next) |
- |
- def test_iteration_sporadic_pages(self): |
- # Some pages have no/incomplete results but have a ``LastEvaluatedKey`` |
- # (for instance, scans with filters), so we need to accommodate that. |
- def sporadic(): |
- # A dict, because Python closures have read-only access to the |
- # reference itself. |
- count = {'value': -1} |
- |
- def _wrapper(limit=10, exclusive_start_key=None): |
- count['value'] = count['value'] + 1 |
- |
- if count['value'] == 0: |
- # Full page. |
- return { |
- 'results': [ |
- 'Result #0', |
- 'Result #1', |
- 'Result #2', |
- 'Result #3', |
- ], |
- 'last_key': 'page-1' |
- } |
- elif count['value'] == 1: |
- # Empty page but continue. |
- return { |
- 'results': [], |
- 'last_key': 'page-2' |
- } |
- elif count['value'] == 2: |
- # Final page. |
- return { |
- 'results': [ |
- 'Result #4', |
- 'Result #5', |
- 'Result #6', |
- ], |
- } |
- |
- return _wrapper |
- |
- results = ResultSet() |
- results.to_call(sporadic(), limit=20) |
- # First page |
- self.assertEqual(next(results), 'Result #0') |
- self.assertEqual(next(results), 'Result #1') |
- self.assertEqual(next(results), 'Result #2') |
- self.assertEqual(next(results), 'Result #3') |
- # Second page (misses!) |
- # Moves on to the third page |
- self.assertEqual(next(results), 'Result #4') |
- self.assertEqual(next(results), 'Result #5') |
- self.assertEqual(next(results), 'Result #6') |
- self.assertRaises(StopIteration, results.next) |
- |
- def test_list(self): |
- self.assertEqual(list(self.results), [ |
- 'Hello john #0', |
- 'Hello john #1', |
- 'Hello john #2', |
- 'Hello john #3', |
- 'Hello john #4', |
- 'Hello john #5', |
- 'Hello john #6', |
- 'Hello john #7', |
- 'Hello john #8', |
- 'Hello john #9', |
- 'Hello john #10', |
- 'Hello john #11', |
- 'Hello john #12' |
- ]) |
- |
- |
-def fake_batch_results(keys): |
- results = [] |
- simulate_unprocessed = True |
- |
- if len(keys) and keys[0] == 'johndoe': |
- simulate_unprocessed = False |
- |
- for key in keys: |
- if simulate_unprocessed and key == 'johndoe': |
- continue |
- |
- results.append("hello %s" % key) |
- |
- retval = { |
- 'results': results, |
- 'last_key': None, |
- } |
- |
- if simulate_unprocessed: |
- retval['unprocessed_keys'] = ['johndoe'] |
- |
- return retval |
- |
- |
-class BatchGetResultSetTestCase(unittest.TestCase): |
- def setUp(self): |
- super(BatchGetResultSetTestCase, self).setUp() |
- self.results = BatchGetResultSet(keys=[ |
- 'alice', |
- 'bob', |
- 'jane', |
- 'johndoe', |
- ]) |
- self.results.to_call(fake_batch_results) |
- |
- def test_fetch_more(self): |
- # First "page". |
- self.results.fetch_more() |
- self.assertEqual(self.results._results, [ |
- 'hello alice', |
- 'hello bob', |
- 'hello jane', |
- ]) |
- self.assertEqual(self.results._keys_left, ['johndoe']) |
- |
- # Second "page". |
- self.results.fetch_more() |
- self.assertEqual(self.results._results, [ |
- 'hello johndoe', |
- ]) |
- |
- # Empty "page". Nothing new gets added |
- self.results.fetch_more() |
- self.assertEqual(self.results._results, []) |
- |
- # Make sure we won't check for results in the future. |
- self.assertFalse(self.results._results_left) |
- |
- def test_fetch_more_empty(self): |
- self.results.to_call(lambda keys: {'results': [], 'last_key': None}) |
- |
- self.results.fetch_more() |
- self.assertEqual(self.results._results, []) |
- self.assertRaises(StopIteration, self.results.next) |
- |
- def test_iteration(self): |
- # First page. |
- self.assertEqual(next(self.results), 'hello alice') |
- self.assertEqual(next(self.results), 'hello bob') |
- self.assertEqual(next(self.results), 'hello jane') |
- self.assertEqual(next(self.results), 'hello johndoe') |
- self.assertRaises(StopIteration, self.results.next) |
- |
- |
-class TableTestCase(unittest.TestCase): |
- def setUp(self): |
- super(TableTestCase, self).setUp() |
- self.users = Table('users', connection=FakeDynamoDBConnection()) |
- self.default_connection = DynamoDBConnection( |
- aws_access_key_id='access_key', |
- aws_secret_access_key='secret_key' |
- ) |
- |
- def test__introspect_schema(self): |
- raw_schema_1 = [ |
- { |
- "AttributeName": "username", |
- "KeyType": "HASH" |
- }, |
- { |
- "AttributeName": "date_joined", |
- "KeyType": "RANGE" |
- } |
- ] |
- raw_attributes_1 = [ |
- { |
- 'AttributeName': 'username', |
- 'AttributeType': 'S' |
- }, |
- { |
- 'AttributeName': 'date_joined', |
- 'AttributeType': 'S' |
- }, |
- ] |
- schema_1 = self.users._introspect_schema(raw_schema_1, raw_attributes_1) |
- self.assertEqual(len(schema_1), 2) |
- self.assertTrue(isinstance(schema_1[0], HashKey)) |
- self.assertEqual(schema_1[0].name, 'username') |
- self.assertTrue(isinstance(schema_1[1], RangeKey)) |
- self.assertEqual(schema_1[1].name, 'date_joined') |
- |
- raw_schema_2 = [ |
- { |
- "AttributeName": "username", |
- "KeyType": "BTREE" |
- }, |
- ] |
- raw_attributes_2 = [ |
- { |
- 'AttributeName': 'username', |
- 'AttributeType': 'S' |
- }, |
- ] |
- self.assertRaises( |
- exceptions.UnknownSchemaFieldError, |
- self.users._introspect_schema, |
- raw_schema_2, |
- raw_attributes_2 |
- ) |
- |
- # Test a complex schema & ensure the types come back correctly. |
- raw_schema_3 = [ |
- { |
- "AttributeName": "user_id", |
- "KeyType": "HASH" |
- }, |
- { |
- "AttributeName": "junk", |
- "KeyType": "RANGE" |
- } |
- ] |
- raw_attributes_3 = [ |
- { |
- 'AttributeName': 'user_id', |
- 'AttributeType': 'N' |
- }, |
- { |
- 'AttributeName': 'junk', |
- 'AttributeType': 'B' |
- }, |
- ] |
- schema_3 = self.users._introspect_schema(raw_schema_3, raw_attributes_3) |
- self.assertEqual(len(schema_3), 2) |
- self.assertTrue(isinstance(schema_3[0], HashKey)) |
- self.assertEqual(schema_3[0].name, 'user_id') |
- self.assertEqual(schema_3[0].data_type, NUMBER) |
- self.assertTrue(isinstance(schema_3[1], RangeKey)) |
- self.assertEqual(schema_3[1].name, 'junk') |
- self.assertEqual(schema_3[1].data_type, BINARY) |
- |
- def test__introspect_indexes(self): |
- raw_indexes_1 = [ |
- { |
- "IndexName": "MostRecentlyJoinedIndex", |
- "KeySchema": [ |
- { |
- "AttributeName": "username", |
- "KeyType": "HASH" |
- }, |
- { |
- "AttributeName": "date_joined", |
- "KeyType": "RANGE" |
- } |
- ], |
- "Projection": { |
- "ProjectionType": "KEYS_ONLY" |
- } |
- }, |
- { |
- "IndexName": "EverybodyIndex", |
- "KeySchema": [ |
- { |
- "AttributeName": "username", |
- "KeyType": "HASH" |
- }, |
- ], |
- "Projection": { |
- "ProjectionType": "ALL" |
- } |
- }, |
- { |
- "IndexName": "GenderIndex", |
- "KeySchema": [ |
- { |
- "AttributeName": "username", |
- "KeyType": "HASH" |
- }, |
- { |
- "AttributeName": "date_joined", |
- "KeyType": "RANGE" |
- } |
- ], |
- "Projection": { |
- "ProjectionType": "INCLUDE", |
- "NonKeyAttributes": [ |
- 'gender', |
- ] |
- } |
- } |
- ] |
- indexes_1 = self.users._introspect_indexes(raw_indexes_1) |
- self.assertEqual(len(indexes_1), 3) |
- self.assertTrue(isinstance(indexes_1[0], KeysOnlyIndex)) |
- self.assertEqual(indexes_1[0].name, 'MostRecentlyJoinedIndex') |
- self.assertEqual(len(indexes_1[0].parts), 2) |
- self.assertTrue(isinstance(indexes_1[1], AllIndex)) |
- self.assertEqual(indexes_1[1].name, 'EverybodyIndex') |
- self.assertEqual(len(indexes_1[1].parts), 1) |
- self.assertTrue(isinstance(indexes_1[2], IncludeIndex)) |
- self.assertEqual(indexes_1[2].name, 'GenderIndex') |
- self.assertEqual(len(indexes_1[2].parts), 2) |
- self.assertEqual(indexes_1[2].includes_fields, ['gender']) |
- |
- raw_indexes_2 = [ |
- { |
- "IndexName": "MostRecentlyJoinedIndex", |
- "KeySchema": [ |
- { |
- "AttributeName": "username", |
- "KeyType": "HASH" |
- }, |
- { |
- "AttributeName": "date_joined", |
- "KeyType": "RANGE" |
- } |
- ], |
- "Projection": { |
- "ProjectionType": "SOMETHING_CRAZY" |
- } |
- }, |
- ] |
- self.assertRaises( |
- exceptions.UnknownIndexFieldError, |
- self.users._introspect_indexes, |
- raw_indexes_2 |
- ) |
- |
- def test_initialization(self): |
- users = Table('users', connection=self.default_connection) |
- self.assertEqual(users.table_name, 'users') |
- self.assertTrue(isinstance(users.connection, DynamoDBConnection)) |
- self.assertEqual(users.throughput['read'], 5) |
- self.assertEqual(users.throughput['write'], 5) |
- self.assertEqual(users.schema, None) |
- self.assertEqual(users.indexes, None) |
- |
- groups = Table('groups', connection=FakeDynamoDBConnection()) |
- self.assertEqual(groups.table_name, 'groups') |
- self.assertTrue(hasattr(groups.connection, 'assert_called_once_with')) |
- |
- def test_create_simple(self): |
- conn = FakeDynamoDBConnection() |
- |
- with mock.patch.object(conn, 'create_table', return_value={}) \ |
- as mock_create_table: |
- retval = Table.create('users', schema=[ |
- HashKey('username'), |
- RangeKey('date_joined', data_type=NUMBER) |
- ], connection=conn) |
- self.assertTrue(retval) |
- |
- self.assertTrue(mock_create_table.called) |
- mock_create_table.assert_called_once_with(attribute_definitions=[ |
- { |
- 'AttributeName': 'username', |
- 'AttributeType': 'S' |
- }, |
- { |
- 'AttributeName': 'date_joined', |
- 'AttributeType': 'N' |
- } |
- ], |
- table_name='users', |
- key_schema=[ |
- { |
- 'KeyType': 'HASH', |
- 'AttributeName': 'username' |
- }, |
- { |
- 'KeyType': 'RANGE', |
- 'AttributeName': 'date_joined' |
- } |
- ], |
- provisioned_throughput={ |
- 'WriteCapacityUnits': 5, |
- 'ReadCapacityUnits': 5 |
- }) |
- |
- def test_create_full(self): |
- conn = FakeDynamoDBConnection() |
- |
- with mock.patch.object(conn, 'create_table', return_value={}) \ |
- as mock_create_table: |
- retval = Table.create('users', schema=[ |
- HashKey('username'), |
- RangeKey('date_joined', data_type=NUMBER) |
- ], throughput={ |
- 'read':20, |
- 'write': 10, |
- }, indexes=[ |
- KeysOnlyIndex('FriendCountIndex', parts=[ |
- RangeKey('friend_count') |
- ]), |
- ], global_indexes=[ |
- GlobalKeysOnlyIndex('FullFriendCountIndex', parts=[ |
- RangeKey('friend_count') |
- ], throughput={ |
- 'read': 10, |
- 'write': 8, |
- }), |
- ], connection=conn) |
- self.assertTrue(retval) |
- |
- self.assertTrue(mock_create_table.called) |
- mock_create_table.assert_called_once_with(attribute_definitions=[ |
- { |
- 'AttributeName': 'username', |
- 'AttributeType': 'S' |
- }, |
- { |
- 'AttributeName': 'date_joined', |
- 'AttributeType': 'N' |
- }, |
- { |
- 'AttributeName': 'friend_count', |
- 'AttributeType': 'S' |
- } |
- ], |
- key_schema=[ |
- { |
- 'KeyType': 'HASH', |
- 'AttributeName': 'username' |
- }, |
- { |
- 'KeyType': 'RANGE', |
- 'AttributeName': 'date_joined' |
- } |
- ], |
- table_name='users', |
- provisioned_throughput={ |
- 'WriteCapacityUnits': 10, |
- 'ReadCapacityUnits': 20 |
- }, |
- global_secondary_indexes=[ |
- { |
- 'KeySchema': [ |
- { |
- 'KeyType': 'RANGE', |
- 'AttributeName': 'friend_count' |
- } |
- ], |
- 'IndexName': 'FullFriendCountIndex', |
- 'Projection': { |
- 'ProjectionType': 'KEYS_ONLY' |
- }, |
- 'ProvisionedThroughput': { |
- 'WriteCapacityUnits': 8, |
- 'ReadCapacityUnits': 10 |
- } |
- } |
- ], |
- local_secondary_indexes=[ |
- { |
- 'KeySchema': [ |
- { |
- 'KeyType': 'RANGE', |
- 'AttributeName': 'friend_count' |
- } |
- ], |
- 'IndexName': 'FriendCountIndex', |
- 'Projection': { |
- 'ProjectionType': 'KEYS_ONLY' |
- } |
- } |
- ]) |
- |
- def test_describe(self): |
- expected = { |
- "Table": { |
- "AttributeDefinitions": [ |
- { |
- "AttributeName": "username", |
- "AttributeType": "S" |
- } |
- ], |
- "ItemCount": 5, |
- "KeySchema": [ |
- { |
- "AttributeName": "username", |
- "KeyType": "HASH" |
- } |
- ], |
- "LocalSecondaryIndexes": [ |
- { |
- "IndexName": "UsernameIndex", |
- "KeySchema": [ |
- { |
- "AttributeName": "username", |
- "KeyType": "HASH" |
- } |
- ], |
- "Projection": { |
- "ProjectionType": "KEYS_ONLY" |
- } |
- } |
- ], |
- "ProvisionedThroughput": { |
- "ReadCapacityUnits": 20, |
- "WriteCapacityUnits": 6 |
- }, |
- "TableName": "Thread", |
- "TableStatus": "ACTIVE" |
- } |
- } |
- |
- with mock.patch.object( |
- self.users.connection, |
- 'describe_table', |
- return_value=expected) as mock_describe: |
- self.assertEqual(self.users.throughput['read'], 5) |
- self.assertEqual(self.users.throughput['write'], 5) |
- self.assertEqual(self.users.schema, None) |
- self.assertEqual(self.users.indexes, None) |
- |
- self.users.describe() |
- |
- self.assertEqual(self.users.throughput['read'], 20) |
- self.assertEqual(self.users.throughput['write'], 6) |
- self.assertEqual(len(self.users.schema), 1) |
- self.assertEqual(isinstance(self.users.schema[0], HashKey), 1) |
- self.assertEqual(len(self.users.indexes), 1) |
- |
- mock_describe.assert_called_once_with('users') |
- |
- def test_update(self): |
- with mock.patch.object( |
- self.users.connection, |
- 'update_table', |
- return_value={}) as mock_update: |
- self.assertEqual(self.users.throughput['read'], 5) |
- self.assertEqual(self.users.throughput['write'], 5) |
- self.users.update(throughput={ |
- 'read': 7, |
- 'write': 2, |
- }) |
- self.assertEqual(self.users.throughput['read'], 7) |
- self.assertEqual(self.users.throughput['write'], 2) |
- |
- mock_update.assert_called_once_with( |
- 'users', |
- global_secondary_index_updates=None, |
- provisioned_throughput={ |
- 'WriteCapacityUnits': 2, |
- 'ReadCapacityUnits': 7 |
- } |
- ) |
- |
- with mock.patch.object( |
- self.users.connection, |
- 'update_table', |
- return_value={}) as mock_update: |
- self.assertEqual(self.users.throughput['read'], 7) |
- self.assertEqual(self.users.throughput['write'], 2) |
- self.users.update(throughput={ |
- 'read': 9, |
- 'write': 5, |
- }, |
- global_indexes={ |
- 'WhateverIndex': { |
- 'read': 6, |
- 'write': 1 |
- }, |
- 'AnotherIndex': { |
- 'read': 1, |
- 'write': 2 |
- } |
- }) |
- self.assertEqual(self.users.throughput['read'], 9) |
- self.assertEqual(self.users.throughput['write'], 5) |
- |
- args, kwargs = mock_update.call_args |
- self.assertEqual(args, ('users',)) |
- self.assertEqual(kwargs['provisioned_throughput'], { |
- 'WriteCapacityUnits': 5, |
- 'ReadCapacityUnits': 9, |
- }) |
- update = kwargs['global_secondary_index_updates'][:] |
- update.sort(key=lambda x: x['Update']['IndexName']) |
- self.assertDictEqual( |
- update[0], |
- { |
- 'Update': { |
- 'IndexName': 'AnotherIndex', |
- 'ProvisionedThroughput': { |
- 'WriteCapacityUnits': 2, |
- 'ReadCapacityUnits': 1 |
- } |
- } |
- }) |
- self.assertDictEqual( |
- update[1], |
- { |
- 'Update': { |
- 'IndexName': 'WhateverIndex', |
- 'ProvisionedThroughput': { |
- 'WriteCapacityUnits': 1, |
- 'ReadCapacityUnits': 6 |
- } |
- } |
- }) |
- |
- def test_create_global_secondary_index(self): |
- with mock.patch.object( |
- self.users.connection, |
- 'update_table', |
- return_value={}) as mock_update: |
- self.users.create_global_secondary_index( |
- global_index=GlobalAllIndex( |
- 'JustCreatedIndex', |
- parts=[ |
- HashKey('requiredHashKey') |
- ], |
- throughput={ |
- 'read': 2, |
- 'write': 2 |
- } |
- ) |
- ) |
- |
- mock_update.assert_called_once_with( |
- 'users', |
- global_secondary_index_updates=[ |
- { |
- 'Create': { |
- 'IndexName': 'JustCreatedIndex', |
- 'KeySchema': [ |
- { |
- 'KeyType': 'HASH', |
- 'AttributeName': 'requiredHashKey' |
- } |
- ], |
- 'Projection': { |
- 'ProjectionType': 'ALL' |
- }, |
- 'ProvisionedThroughput': { |
- 'WriteCapacityUnits': 2, |
- 'ReadCapacityUnits': 2 |
- } |
- } |
- } |
- ], |
- attribute_definitions=[ |
- { |
- 'AttributeName': 'requiredHashKey', |
- 'AttributeType': 'S' |
- } |
- ] |
- ) |
- |
- def test_delete_global_secondary_index(self): |
- with mock.patch.object( |
- self.users.connection, |
- 'update_table', |
- return_value={}) as mock_update: |
- self.users.delete_global_secondary_index('RandomGSIIndex') |
- |
- mock_update.assert_called_once_with( |
- 'users', |
- global_secondary_index_updates=[ |
- { |
- 'Delete': { |
- 'IndexName': 'RandomGSIIndex', |
- } |
- } |
- ] |
- ) |
- |
- def test_update_global_secondary_index(self): |
- # Updating a single global secondary index |
- with mock.patch.object( |
- self.users.connection, |
- 'update_table', |
- return_value={}) as mock_update: |
- self.users.update_global_secondary_index(global_indexes={ |
- 'A_IndexToBeUpdated': { |
- 'read': 5, |
- 'write': 5 |
- } |
- }) |
- |
- mock_update.assert_called_once_with( |
- 'users', |
- global_secondary_index_updates=[ |
- { |
- 'Update': { |
- 'IndexName': 'A_IndexToBeUpdated', |
- "ProvisionedThroughput": { |
- "ReadCapacityUnits": 5, |
- "WriteCapacityUnits": 5 |
- }, |
- } |
- } |
- ] |
- ) |
- |
- # Updating multiple global secondary indexes |
- with mock.patch.object( |
- self.users.connection, |
- 'update_table', |
- return_value={}) as mock_update: |
- self.users.update_global_secondary_index(global_indexes={ |
- 'A_IndexToBeUpdated': { |
- 'read': 5, |
- 'write': 5 |
- }, |
- 'B_IndexToBeUpdated': { |
- 'read': 9, |
- 'write': 9 |
- } |
- }) |
- |
- args, kwargs = mock_update.call_args |
- self.assertEqual(args, ('users',)) |
- update = kwargs['global_secondary_index_updates'][:] |
- update.sort(key=lambda x: x['Update']['IndexName']) |
- self.assertDictEqual( |
- update[0], |
- { |
- 'Update': { |
- 'IndexName': 'A_IndexToBeUpdated', |
- 'ProvisionedThroughput': { |
- 'WriteCapacityUnits': 5, |
- 'ReadCapacityUnits': 5 |
- } |
- } |
- }) |
- self.assertDictEqual( |
- update[1], |
- { |
- 'Update': { |
- 'IndexName': 'B_IndexToBeUpdated', |
- 'ProvisionedThroughput': { |
- 'WriteCapacityUnits': 9, |
- 'ReadCapacityUnits': 9 |
- } |
- } |
- }) |
- |
- def test_delete(self): |
- with mock.patch.object( |
- self.users.connection, |
- 'delete_table', |
- return_value={}) as mock_delete: |
- self.assertTrue(self.users.delete()) |
- |
- mock_delete.assert_called_once_with('users') |
- |
- def test_get_item(self): |
- expected = { |
- 'Item': { |
- 'username': {'S': 'johndoe'}, |
- 'first_name': {'S': 'John'}, |
- 'last_name': {'S': 'Doe'}, |
- 'date_joined': {'N': '1366056668'}, |
- 'friend_count': {'N': '3'}, |
- 'friends': {'SS': ['alice', 'bob', 'jane']}, |
- } |
- } |
- |
- with mock.patch.object( |
- self.users.connection, |
- 'get_item', |
- return_value=expected) as mock_get_item: |
- item = self.users.get_item(username='johndoe') |
- self.assertEqual(item['username'], 'johndoe') |
- self.assertEqual(item['first_name'], 'John') |
- |
- mock_get_item.assert_called_once_with('users', { |
- 'username': {'S': 'johndoe'} |
- }, consistent_read=False, attributes_to_get=None) |
- |
- with mock.patch.object( |
- self.users.connection, |
- 'get_item', |
- return_value=expected) as mock_get_item: |
- item = self.users.get_item(username='johndoe', attributes=[ |
- 'username', |
- 'first_name', |
- ]) |
- |
- mock_get_item.assert_called_once_with('users', { |
- 'username': {'S': 'johndoe'} |
- }, consistent_read=False, attributes_to_get=['username', 'first_name']) |
- |
- def test_has_item(self): |
- expected = { |
- 'Item': { |
- 'username': {'S': 'johndoe'}, |
- 'first_name': {'S': 'John'}, |
- 'last_name': {'S': 'Doe'}, |
- 'date_joined': {'N': '1366056668'}, |
- 'friend_count': {'N': '3'}, |
- 'friends': {'SS': ['alice', 'bob', 'jane']}, |
- } |
- } |
- |
- with mock.patch.object( |
- self.users.connection, |
- 'get_item', |
- return_value=expected) as mock_get_item: |
- found = self.users.has_item(username='johndoe') |
- self.assertTrue(found) |
- |
- with mock.patch.object( |
- self.users.connection, |
- 'get_item') as mock_get_item: |
- mock_get_item.side_effect = JSONResponseError("Nope.", None, None) |
- found = self.users.has_item(username='mrsmith') |
- self.assertFalse(found) |
- |
- def test_lookup_hash(self): |
- """Tests the "lookup" function with just a hash key""" |
- expected = { |
- 'Item': { |
- 'username': {'S': 'johndoe'}, |
- 'first_name': {'S': 'John'}, |
- 'last_name': {'S': 'Doe'}, |
- 'date_joined': {'N': '1366056668'}, |
- 'friend_count': {'N': '3'}, |
- 'friends': {'SS': ['alice', 'bob', 'jane']}, |
- } |
- } |
- |
- # Set the Schema |
- self.users.schema = [ |
- HashKey('username'), |
- RangeKey('date_joined', data_type=NUMBER), |
- ] |
- |
- with mock.patch.object( |
- self.users, |
- 'get_item', |
- return_value=expected) as mock_get_item: |
- self.users.lookup('johndoe') |
- |
- mock_get_item.assert_called_once_with( |
- username= 'johndoe') |
- |
- def test_lookup_hash_and_range(self): |
- """Test the "lookup" function with a hash and range key""" |
- expected = { |
- 'Item': { |
- 'username': {'S': 'johndoe'}, |
- 'first_name': {'S': 'John'}, |
- 'last_name': {'S': 'Doe'}, |
- 'date_joined': {'N': '1366056668'}, |
- 'friend_count': {'N': '3'}, |
- 'friends': {'SS': ['alice', 'bob', 'jane']}, |
- } |
- } |
- |
- # Set the Schema |
- self.users.schema = [ |
- HashKey('username'), |
- RangeKey('date_joined', data_type=NUMBER), |
- ] |
- |
- with mock.patch.object( |
- self.users, |
- 'get_item', |
- return_value=expected) as mock_get_item: |
- self.users.lookup('johndoe', 1366056668) |
- |
- mock_get_item.assert_called_once_with( |
- username= 'johndoe', |
- date_joined= 1366056668) |
- |
- def test_put_item(self): |
- with mock.patch.object( |
- self.users.connection, |
- 'put_item', |
- return_value={}) as mock_put_item: |
- self.users.put_item(data={ |
- 'username': 'johndoe', |
- 'last_name': 'Doe', |
- 'date_joined': 12345, |
- }) |
- |
- mock_put_item.assert_called_once_with('users', { |
- 'username': {'S': 'johndoe'}, |
- 'last_name': {'S': 'Doe'}, |
- 'date_joined': {'N': '12345'} |
- }, expected={ |
- 'username': { |
- 'Exists': False, |
- }, |
- 'last_name': { |
- 'Exists': False, |
- }, |
- 'date_joined': { |
- 'Exists': False, |
- } |
- }) |
- |
- def test_private_put_item(self): |
- with mock.patch.object( |
- self.users.connection, |
- 'put_item', |
- return_value={}) as mock_put_item: |
- self.users._put_item({'some': 'data'}) |
- |
- mock_put_item.assert_called_once_with('users', {'some': 'data'}) |
- |
- def test_private_update_item(self): |
- with mock.patch.object( |
- self.users.connection, |
- 'update_item', |
- return_value={}) as mock_update_item: |
- self.users._update_item({ |
- 'username': 'johndoe' |
- }, { |
- 'some': 'data', |
- }) |
- |
- mock_update_item.assert_called_once_with('users', { |
- 'username': {'S': 'johndoe'}, |
- }, { |
- 'some': 'data', |
- }) |
- |
- def test_delete_item(self): |
- with mock.patch.object( |
- self.users.connection, |
- 'delete_item', |
- return_value={}) as mock_delete_item: |
- self.assertTrue(self.users.delete_item(username='johndoe', date_joined=23456)) |
- |
- mock_delete_item.assert_called_once_with('users', { |
- 'username': { |
- 'S': 'johndoe' |
- }, |
- 'date_joined': { |
- 'N': '23456' |
- } |
- }, expected=None, conditional_operator=None) |
- |
- def test_delete_item_conditionally(self): |
- with mock.patch.object( |
- self.users.connection, |
- 'delete_item', |
- return_value={}) as mock_delete_item: |
- self.assertTrue(self.users.delete_item(expected={'balance__eq': 0}, |
- username='johndoe', date_joined=23456)) |
- |
- mock_delete_item.assert_called_once_with('users', { |
- 'username': { |
- 'S': 'johndoe' |
- }, |
- 'date_joined': { |
- 'N': '23456' |
- } |
- }, |
- expected={ |
- 'balance': { |
- 'ComparisonOperator': 'EQ', 'AttributeValueList': [{'N': '0'}] |
- }, |
- }, |
- conditional_operator=None) |
- |
- def side_effect(*args, **kwargs): |
- raise exceptions.ConditionalCheckFailedException(400, '', {}) |
- |
- with mock.patch.object( |
- self.users.connection, |
- 'delete_item', |
- side_effect=side_effect) as mock_delete_item: |
- self.assertFalse(self.users.delete_item(expected={'balance__eq': 0}, |
- username='johndoe', date_joined=23456)) |
- |
- def test_get_key_fields_no_schema_populated(self): |
- expected = { |
- "Table": { |
- "AttributeDefinitions": [ |
- { |
- "AttributeName": "username", |
- "AttributeType": "S" |
- }, |
- { |
- "AttributeName": "date_joined", |
- "AttributeType": "N" |
- } |
- ], |
- "ItemCount": 5, |
- "KeySchema": [ |
- { |
- "AttributeName": "username", |
- "KeyType": "HASH" |
- }, |
- { |
- "AttributeName": "date_joined", |
- "KeyType": "RANGE" |
- } |
- ], |
- "LocalSecondaryIndexes": [ |
- { |
- "IndexName": "UsernameIndex", |
- "KeySchema": [ |
- { |
- "AttributeName": "username", |
- "KeyType": "HASH" |
- } |
- ], |
- "Projection": { |
- "ProjectionType": "KEYS_ONLY" |
- } |
- } |
- ], |
- "ProvisionedThroughput": { |
- "ReadCapacityUnits": 20, |
- "WriteCapacityUnits": 6 |
- }, |
- "TableName": "Thread", |
- "TableStatus": "ACTIVE" |
- } |
- } |
- |
- with mock.patch.object( |
- self.users.connection, |
- 'describe_table', |
- return_value=expected) as mock_describe: |
- self.assertEqual(self.users.schema, None) |
- |
- key_fields = self.users.get_key_fields() |
- self.assertEqual(key_fields, ['username', 'date_joined']) |
- |
- self.assertEqual(len(self.users.schema), 2) |
- |
- mock_describe.assert_called_once_with('users') |
- |
- def test_batch_write_no_writes(self): |
- with mock.patch.object( |
- self.users.connection, |
- 'batch_write_item', |
- return_value={}) as mock_batch: |
- with self.users.batch_write() as batch: |
- pass |
- |
- self.assertFalse(mock_batch.called) |
- |
- def test_batch_write(self): |
- with mock.patch.object( |
- self.users.connection, |
- 'batch_write_item', |
- return_value={}) as mock_batch: |
- with self.users.batch_write() as batch: |
- batch.put_item(data={ |
- 'username': 'jane', |
- 'date_joined': 12342547 |
- }) |
- batch.delete_item(username='johndoe') |
- batch.put_item(data={ |
- 'username': 'alice', |
- 'date_joined': 12342888 |
- }) |
- |
- mock_batch.assert_called_once_with({ |
- 'users': [ |
- { |
- 'PutRequest': { |
- 'Item': { |
- 'username': {'S': 'jane'}, |
- 'date_joined': {'N': '12342547'} |
- } |
- } |
- }, |
- { |
- 'PutRequest': { |
- 'Item': { |
- 'username': {'S': 'alice'}, |
- 'date_joined': {'N': '12342888'} |
- } |
- } |
- }, |
- { |
- 'DeleteRequest': { |
- 'Key': { |
- 'username': {'S': 'johndoe'}, |
- } |
- } |
- }, |
- ] |
- }) |
- |
- def test_batch_write_dont_swallow_exceptions(self): |
- with mock.patch.object( |
- self.users.connection, |
- 'batch_write_item', |
- return_value={}) as mock_batch: |
- try: |
- with self.users.batch_write() as batch: |
- raise Exception('OH NOES') |
- except Exception as e: |
- self.assertEqual(str(e), 'OH NOES') |
- |
- self.assertFalse(mock_batch.called) |
- |
- def test_batch_write_flushing(self): |
- with mock.patch.object( |
- self.users.connection, |
- 'batch_write_item', |
- return_value={}) as mock_batch: |
- with self.users.batch_write() as batch: |
- batch.put_item(data={ |
- 'username': 'jane', |
- 'date_joined': 12342547 |
- }) |
- # This would only be enough for one batch. |
- batch.delete_item(username='johndoe1') |
- batch.delete_item(username='johndoe2') |
- batch.delete_item(username='johndoe3') |
- batch.delete_item(username='johndoe4') |
- batch.delete_item(username='johndoe5') |
- batch.delete_item(username='johndoe6') |
- batch.delete_item(username='johndoe7') |
- batch.delete_item(username='johndoe8') |
- batch.delete_item(username='johndoe9') |
- batch.delete_item(username='johndoe10') |
- batch.delete_item(username='johndoe11') |
- batch.delete_item(username='johndoe12') |
- batch.delete_item(username='johndoe13') |
- batch.delete_item(username='johndoe14') |
- batch.delete_item(username='johndoe15') |
- batch.delete_item(username='johndoe16') |
- batch.delete_item(username='johndoe17') |
- batch.delete_item(username='johndoe18') |
- batch.delete_item(username='johndoe19') |
- batch.delete_item(username='johndoe20') |
- batch.delete_item(username='johndoe21') |
- batch.delete_item(username='johndoe22') |
- batch.delete_item(username='johndoe23') |
- |
- # We're only at 24 items. No flushing yet. |
- self.assertEqual(mock_batch.call_count, 0) |
- |
- # This pushes it over the edge. A flush happens then we start |
- # queuing objects again. |
- batch.delete_item(username='johndoe24') |
- self.assertEqual(mock_batch.call_count, 1) |
- # Since we add another, there's enough for a second call to |
- # flush. |
- batch.delete_item(username='johndoe25') |
- |
- self.assertEqual(mock_batch.call_count, 2) |
- |
- def test_batch_write_unprocessed_items(self): |
- unprocessed = { |
- 'UnprocessedItems': { |
- 'users': [ |
- { |
- 'PutRequest': { |
- 'username': { |
- 'S': 'jane', |
- }, |
- 'date_joined': { |
- 'N': 12342547 |
- } |
- }, |
- }, |
- ], |
- }, |
- } |
- |
- # Test enqueuing the unprocessed bits. |
- with mock.patch.object( |
- self.users.connection, |
- 'batch_write_item', |
- return_value=unprocessed) as mock_batch: |
- with self.users.batch_write() as batch: |
- self.assertEqual(len(batch._unprocessed), 0) |
- |
- # Trash the ``resend_unprocessed`` method so that we don't |
- # infinite loop forever here. |
- batch.resend_unprocessed = lambda: True |
- |
- batch.put_item(data={ |
- 'username': 'jane', |
- 'date_joined': 12342547 |
- }) |
- batch.delete_item(username='johndoe') |
- batch.put_item(data={ |
- 'username': 'alice', |
- 'date_joined': 12342888 |
- }) |
- |
- self.assertEqual(len(batch._unprocessed), 1) |
- |
- # Now test resending those unprocessed items. |
- with mock.patch.object( |
- self.users.connection, |
- 'batch_write_item', |
- return_value={}) as mock_batch: |
- with self.users.batch_write() as batch: |
- self.assertEqual(len(batch._unprocessed), 0) |
- |
- # Toss in faked unprocessed items, as though a previous batch |
- # had failed. |
- batch._unprocessed = [ |
- { |
- 'PutRequest': { |
- 'username': { |
- 'S': 'jane', |
- }, |
- 'date_joined': { |
- 'N': 12342547 |
- } |
- }, |
- }, |
- ] |
- |
- batch.put_item(data={ |
- 'username': 'jane', |
- 'date_joined': 12342547 |
- }) |
- batch.delete_item(username='johndoe') |
- batch.put_item(data={ |
- 'username': 'alice', |
- 'date_joined': 12342888 |
- }) |
- |
- # Flush, to make sure everything has been processed. |
- # Unprocessed items should still be hanging around. |
- batch.flush() |
- self.assertEqual(len(batch._unprocessed), 1) |
- |
- # Post-exit, this should be emptied. |
- self.assertEqual(len(batch._unprocessed), 0) |
- |
- def test__build_filters(self): |
- filters = self.users._build_filters({ |
- 'username__eq': 'johndoe', |
- 'date_joined__gte': 1234567, |
- 'age__in': [30, 31, 32, 33], |
- 'last_name__between': ['danzig', 'only'], |
- 'first_name__null': False, |
- 'gender__null': True, |
- }, using=FILTER_OPERATORS) |
- self.assertEqual(filters, { |
- 'username': { |
- 'AttributeValueList': [ |
- { |
- 'S': 'johndoe', |
- }, |
- ], |
- 'ComparisonOperator': 'EQ', |
- }, |
- 'date_joined': { |
- 'AttributeValueList': [ |
- { |
- 'N': '1234567', |
- }, |
- ], |
- 'ComparisonOperator': 'GE', |
- }, |
- 'age': { |
- 'AttributeValueList': [ |
- {'N': '30'}, |
- {'N': '31'}, |
- {'N': '32'}, |
- {'N': '33'}, |
- ], |
- 'ComparisonOperator': 'IN', |
- }, |
- 'last_name': { |
- 'AttributeValueList': [{'S': 'danzig'}, {'S': 'only'}], |
- 'ComparisonOperator': 'BETWEEN', |
- }, |
- 'first_name': { |
- 'ComparisonOperator': 'NOT_NULL' |
- }, |
- 'gender': { |
- 'ComparisonOperator': 'NULL' |
- }, |
- }) |
- |
- self.assertRaises(exceptions.UnknownFilterTypeError, |
- self.users._build_filters, |
- { |
- 'darling__die': True, |
- } |
- ) |
- |
- q_filters = self.users._build_filters({ |
- 'username__eq': 'johndoe', |
- 'date_joined__gte': 1234567, |
- 'last_name__between': ['danzig', 'only'], |
- 'gender__beginswith': 'm', |
- }, using=QUERY_OPERATORS) |
- self.assertEqual(q_filters, { |
- 'username': { |
- 'AttributeValueList': [ |
- { |
- 'S': 'johndoe', |
- }, |
- ], |
- 'ComparisonOperator': 'EQ', |
- }, |
- 'date_joined': { |
- 'AttributeValueList': [ |
- { |
- 'N': '1234567', |
- }, |
- ], |
- 'ComparisonOperator': 'GE', |
- }, |
- 'last_name': { |
- 'AttributeValueList': [{'S': 'danzig'}, {'S': 'only'}], |
- 'ComparisonOperator': 'BETWEEN', |
- }, |
- 'gender': { |
- 'AttributeValueList': [{'S': 'm'}], |
- 'ComparisonOperator': 'BEGINS_WITH', |
- }, |
- }) |
- |
- self.assertRaises(exceptions.UnknownFilterTypeError, |
- self.users._build_filters, |
- { |
- 'darling__die': True, |
- }, |
- using=QUERY_OPERATORS |
- ) |
- self.assertRaises(exceptions.UnknownFilterTypeError, |
- self.users._build_filters, |
- { |
- 'first_name__null': True, |
- }, |
- using=QUERY_OPERATORS |
- ) |
- |
- def test_private_query(self): |
- expected = { |
- "ConsumedCapacity": { |
- "CapacityUnits": 0.5, |
- "TableName": "users" |
- }, |
- "Count": 4, |
- "Items": [ |
- { |
- 'username': {'S': 'johndoe'}, |
- 'first_name': {'S': 'John'}, |
- 'last_name': {'S': 'Doe'}, |
- 'date_joined': {'N': '1366056668'}, |
- 'friend_count': {'N': '3'}, |
- 'friends': {'SS': ['alice', 'bob', 'jane']}, |
- }, |
- { |
- 'username': {'S': 'jane'}, |
- 'first_name': {'S': 'Jane'}, |
- 'last_name': {'S': 'Doe'}, |
- 'date_joined': {'N': '1366057777'}, |
- 'friend_count': {'N': '2'}, |
- 'friends': {'SS': ['alice', 'johndoe']}, |
- }, |
- { |
- 'username': {'S': 'alice'}, |
- 'first_name': {'S': 'Alice'}, |
- 'last_name': {'S': 'Expert'}, |
- 'date_joined': {'N': '1366056680'}, |
- 'friend_count': {'N': '1'}, |
- 'friends': {'SS': ['jane']}, |
- }, |
- { |
- 'username': {'S': 'bob'}, |
- 'first_name': {'S': 'Bob'}, |
- 'last_name': {'S': 'Smith'}, |
- 'date_joined': {'N': '1366056888'}, |
- 'friend_count': {'N': '1'}, |
- 'friends': {'SS': ['johndoe']}, |
- }, |
- ], |
- "ScannedCount": 4 |
- } |
- |
- with mock.patch.object( |
- self.users.connection, |
- 'query', |
- return_value=expected) as mock_query: |
- results = self.users._query( |
- limit=4, |
- reverse=True, |
- username__between=['aaa', 'mmm'] |
- ) |
- usernames = [res['username'] for res in results['results']] |
- self.assertEqual(usernames, ['johndoe', 'jane', 'alice', 'bob']) |
- self.assertEqual(len(results['results']), 4) |
- self.assertEqual(results['last_key'], None) |
- |
- mock_query.assert_called_once_with('users', |
- consistent_read=False, |
- scan_index_forward=False, |
- index_name=None, |
- attributes_to_get=None, |
- limit=4, |
- key_conditions={ |
- 'username': { |
- 'AttributeValueList': [{'S': 'aaa'}, {'S': 'mmm'}], |
- 'ComparisonOperator': 'BETWEEN', |
- } |
- }, |
- select=None, |
- query_filter=None, |
- conditional_operator=None |
- ) |
- |
- # Now alter the expected. |
- expected['LastEvaluatedKey'] = { |
- 'username': { |
- 'S': 'johndoe', |
- }, |
- } |
- |
- with mock.patch.object( |
- self.users.connection, |
- 'query', |
- return_value=expected) as mock_query_2: |
- results = self.users._query( |
- limit=4, |
- reverse=True, |
- username__between=['aaa', 'mmm'], |
- exclusive_start_key={ |
- 'username': 'adam', |
- }, |
- consistent=True, |
- query_filter=None, |
- conditional_operator='AND' |
- ) |
- usernames = [res['username'] for res in results['results']] |
- self.assertEqual(usernames, ['johndoe', 'jane', 'alice', 'bob']) |
- self.assertEqual(len(results['results']), 4) |
- self.assertEqual(results['last_key'], {'username': 'johndoe'}) |
- |
- mock_query_2.assert_called_once_with('users', |
- key_conditions={ |
- 'username': { |
- 'AttributeValueList': [{'S': 'aaa'}, {'S': 'mmm'}], |
- 'ComparisonOperator': 'BETWEEN', |
- } |
- }, |
- index_name=None, |
- attributes_to_get=None, |
- scan_index_forward=False, |
- limit=4, |
- exclusive_start_key={ |
- 'username': { |
- 'S': 'adam', |
- }, |
- }, |
- consistent_read=True, |
- select=None, |
- query_filter=None, |
- conditional_operator='AND' |
- ) |
- |
- def test_private_scan(self): |
- expected = { |
- "ConsumedCapacity": { |
- "CapacityUnits": 0.5, |
- "TableName": "users" |
- }, |
- "Count": 4, |
- "Items": [ |
- { |
- 'username': {'S': 'alice'}, |
- 'first_name': {'S': 'Alice'}, |
- 'last_name': {'S': 'Expert'}, |
- 'date_joined': {'N': '1366056680'}, |
- 'friend_count': {'N': '1'}, |
- 'friends': {'SS': ['jane']}, |
- }, |
- { |
- 'username': {'S': 'bob'}, |
- 'first_name': {'S': 'Bob'}, |
- 'last_name': {'S': 'Smith'}, |
- 'date_joined': {'N': '1366056888'}, |
- 'friend_count': {'N': '1'}, |
- 'friends': {'SS': ['johndoe']}, |
- }, |
- { |
- 'username': {'S': 'jane'}, |
- 'first_name': {'S': 'Jane'}, |
- 'last_name': {'S': 'Doe'}, |
- 'date_joined': {'N': '1366057777'}, |
- 'friend_count': {'N': '2'}, |
- 'friends': {'SS': ['alice', 'johndoe']}, |
- }, |
- ], |
- "ScannedCount": 4 |
- } |
- |
- with mock.patch.object( |
- self.users.connection, |
- 'scan', |
- return_value=expected) as mock_scan: |
- results = self.users._scan( |
- limit=2, |
- friend_count__lte=2 |
- ) |
- usernames = [res['username'] for res in results['results']] |
- self.assertEqual(usernames, ['alice', 'bob', 'jane']) |
- self.assertEqual(len(results['results']), 3) |
- self.assertEqual(results['last_key'], None) |
- |
- mock_scan.assert_called_once_with('users', |
- scan_filter={ |
- 'friend_count': { |
- 'AttributeValueList': [{'N': '2'}], |
- 'ComparisonOperator': 'LE', |
- } |
- }, |
- limit=2, |
- segment=None, |
- attributes_to_get=None, |
- total_segments=None, |
- conditional_operator=None |
- ) |
- |
- # Now alter the expected. |
- expected['LastEvaluatedKey'] = { |
- 'username': { |
- 'S': 'jane', |
- }, |
- } |
- |
- with mock.patch.object( |
- self.users.connection, |
- 'scan', |
- return_value=expected) as mock_scan_2: |
- results = self.users._scan( |
- limit=3, |
- friend_count__lte=2, |
- exclusive_start_key={ |
- 'username': 'adam', |
- }, |
- segment=None, |
- total_segments=None |
- ) |
- usernames = [res['username'] for res in results['results']] |
- self.assertEqual(usernames, ['alice', 'bob', 'jane']) |
- self.assertEqual(len(results['results']), 3) |
- self.assertEqual(results['last_key'], {'username': 'jane'}) |
- |
- mock_scan_2.assert_called_once_with('users', |
- scan_filter={ |
- 'friend_count': { |
- 'AttributeValueList': [{'N': '2'}], |
- 'ComparisonOperator': 'LE', |
- } |
- }, |
- limit=3, |
- exclusive_start_key={ |
- 'username': { |
- 'S': 'adam', |
- }, |
- }, |
- segment=None, |
- attributes_to_get=None, |
- total_segments=None, |
- conditional_operator=None |
- ) |
- |
- def test_query(self): |
- items_1 = { |
- 'results': [ |
- Item(self.users, data={ |
- 'username': 'johndoe', |
- 'first_name': 'John', |
- 'last_name': 'Doe', |
- }), |
- Item(self.users, data={ |
- 'username': 'jane', |
- 'first_name': 'Jane', |
- 'last_name': 'Doe', |
- }), |
- ], |
- 'last_key': 'jane', |
- } |
- |
- results = self.users.query_2(last_name__eq='Doe') |
- self.assertTrue(isinstance(results, ResultSet)) |
- self.assertEqual(len(results._results), 0) |
- self.assertEqual(results.the_callable, self.users._query) |
- |
- with mock.patch.object( |
- results, |
- 'the_callable', |
- return_value=items_1) as mock_query: |
- res_1 = next(results) |
- # Now it should be populated. |
- self.assertEqual(len(results._results), 2) |
- self.assertEqual(res_1['username'], 'johndoe') |
- res_2 = next(results) |
- self.assertEqual(res_2['username'], 'jane') |
- |
- self.assertEqual(mock_query.call_count, 1) |
- |
- items_2 = { |
- 'results': [ |
- Item(self.users, data={ |
- 'username': 'foodoe', |
- 'first_name': 'Foo', |
- 'last_name': 'Doe', |
- }), |
- ], |
- } |
- |
- with mock.patch.object( |
- results, |
- 'the_callable', |
- return_value=items_2) as mock_query_2: |
- res_3 = next(results) |
- # New results should have been found. |
- self.assertEqual(len(results._results), 1) |
- self.assertEqual(res_3['username'], 'foodoe') |
- |
- self.assertRaises(StopIteration, results.next) |
- |
- self.assertEqual(mock_query_2.call_count, 1) |
- |
- def test_query_with_specific_attributes(self): |
- items_1 = { |
- 'results': [ |
- Item(self.users, data={ |
- 'username': 'johndoe', |
- }), |
- Item(self.users, data={ |
- 'username': 'jane', |
- }), |
- ], |
- 'last_key': 'jane', |
- } |
- |
- results = self.users.query_2(last_name__eq='Doe', |
- attributes=['username']) |
- self.assertTrue(isinstance(results, ResultSet)) |
- self.assertEqual(len(results._results), 0) |
- self.assertEqual(results.the_callable, self.users._query) |
- |
- with mock.patch.object( |
- results, |
- 'the_callable', |
- return_value=items_1) as mock_query: |
- res_1 = next(results) |
- # Now it should be populated. |
- self.assertEqual(len(results._results), 2) |
- self.assertEqual(res_1['username'], 'johndoe') |
- self.assertEqual(list(res_1.keys()), ['username']) |
- res_2 = next(results) |
- self.assertEqual(res_2['username'], 'jane') |
- |
- self.assertEqual(mock_query.call_count, 1) |
- |
- def test_scan(self): |
- items_1 = { |
- 'results': [ |
- Item(self.users, data={ |
- 'username': 'johndoe', |
- 'first_name': 'John', |
- 'last_name': 'Doe', |
- }), |
- Item(self.users, data={ |
- 'username': 'jane', |
- 'first_name': 'Jane', |
- 'last_name': 'Doe', |
- }), |
- ], |
- 'last_key': 'jane', |
- } |
- |
- results = self.users.scan(last_name__eq='Doe') |
- self.assertTrue(isinstance(results, ResultSet)) |
- self.assertEqual(len(results._results), 0) |
- self.assertEqual(results.the_callable, self.users._scan) |
- |
- with mock.patch.object( |
- results, |
- 'the_callable', |
- return_value=items_1) as mock_scan: |
- res_1 = next(results) |
- # Now it should be populated. |
- self.assertEqual(len(results._results), 2) |
- self.assertEqual(res_1['username'], 'johndoe') |
- res_2 = next(results) |
- self.assertEqual(res_2['username'], 'jane') |
- |
- self.assertEqual(mock_scan.call_count, 1) |
- |
- items_2 = { |
- 'results': [ |
- Item(self.users, data={ |
- 'username': 'zoeydoe', |
- 'first_name': 'Zoey', |
- 'last_name': 'Doe', |
- }), |
- ], |
- } |
- |
- with mock.patch.object( |
- results, |
- 'the_callable', |
- return_value=items_2) as mock_scan_2: |
- res_3 = next(results) |
- # New results should have been found. |
- self.assertEqual(len(results._results), 1) |
- self.assertEqual(res_3['username'], 'zoeydoe') |
- |
- self.assertRaises(StopIteration, results.next) |
- |
- self.assertEqual(mock_scan_2.call_count, 1) |
- |
- def test_scan_with_specific_attributes(self): |
- items_1 = { |
- 'results': [ |
- Item(self.users, data={ |
- 'username': 'johndoe', |
- }), |
- Item(self.users, data={ |
- 'username': 'jane', |
- }), |
- ], |
- 'last_key': 'jane', |
- } |
- |
- results = self.users.scan(attributes=['username']) |
- self.assertTrue(isinstance(results, ResultSet)) |
- self.assertEqual(len(results._results), 0) |
- self.assertEqual(results.the_callable, self.users._scan) |
- |
- with mock.patch.object( |
- results, |
- 'the_callable', |
- return_value=items_1) as mock_query: |
- res_1 = next(results) |
- # Now it should be populated. |
- self.assertEqual(len(results._results), 2) |
- self.assertEqual(res_1['username'], 'johndoe') |
- self.assertEqual(list(res_1.keys()), ['username']) |
- res_2 = next(results) |
- self.assertEqual(res_2['username'], 'jane') |
- |
- self.assertEqual(mock_query.call_count, 1) |
- |
- def test_count(self): |
- expected = { |
- "Table": { |
- "AttributeDefinitions": [ |
- { |
- "AttributeName": "username", |
- "AttributeType": "S" |
- } |
- ], |
- "ItemCount": 5, |
- "KeySchema": [ |
- { |
- "AttributeName": "username", |
- "KeyType": "HASH" |
- } |
- ], |
- "LocalSecondaryIndexes": [ |
- { |
- "IndexName": "UsernameIndex", |
- "KeySchema": [ |
- { |
- "AttributeName": "username", |
- "KeyType": "HASH" |
- } |
- ], |
- "Projection": { |
- "ProjectionType": "KEYS_ONLY" |
- } |
- } |
- ], |
- "ProvisionedThroughput": { |
- "ReadCapacityUnits": 20, |
- "WriteCapacityUnits": 6 |
- }, |
- "TableName": "Thread", |
- "TableStatus": "ACTIVE" |
- } |
- } |
- |
- with mock.patch.object( |
- self.users, |
- 'describe', |
- return_value=expected) as mock_count: |
- self.assertEqual(self.users.count(), 5) |
- |
- def test_query_count_simple(self): |
- expected_0 = { |
- 'Count': 0.0, |
- } |
- |
- expected_1 = { |
- 'Count': 10.0, |
- } |
- |
- with mock.patch.object( |
- self.users.connection, |
- 'query', |
- return_value=expected_0) as mock_query: |
- results = self.users.query_count(username__eq='notmyname') |
- self.assertTrue(isinstance(results, int)) |
- self.assertEqual(results, 0) |
- self.assertEqual(mock_query.call_count, 1) |
- self.assertIn('scan_index_forward', mock_query.call_args[1]) |
- self.assertEqual(True, mock_query.call_args[1]['scan_index_forward']) |
- self.assertIn('limit', mock_query.call_args[1]) |
- self.assertEqual(None, mock_query.call_args[1]['limit']) |
- |
- with mock.patch.object( |
- self.users.connection, |
- 'query', |
- return_value=expected_1) as mock_query: |
- results = self.users.query_count(username__gt='somename', consistent=True, scan_index_forward=False, limit=10) |
- self.assertTrue(isinstance(results, int)) |
- self.assertEqual(results, 10) |
- self.assertEqual(mock_query.call_count, 1) |
- self.assertIn('scan_index_forward', mock_query.call_args[1]) |
- self.assertEqual(False, mock_query.call_args[1]['scan_index_forward']) |
- self.assertIn('limit', mock_query.call_args[1]) |
- self.assertEqual(10, mock_query.call_args[1]['limit']) |
- |
- def test_query_count_paginated(self): |
- def return_side_effect(*args, **kwargs): |
- if kwargs.get('exclusive_start_key'): |
- return {'Count': 10, 'LastEvaluatedKey': None} |
- else: |
- return { |
- 'Count': 20, |
- 'LastEvaluatedKey': { |
- 'username': { |
- 'S': 'johndoe' |
- }, |
- 'date_joined': { |
- 'N': '4118642633' |
- } |
- } |
- } |
- |
- with mock.patch.object( |
- self.users.connection, |
- 'query', |
- side_effect=return_side_effect |
- ) as mock_query: |
- count = self.users.query_count(username__eq='johndoe') |
- self.assertTrue(isinstance(count, int)) |
- self.assertEqual(30, count) |
- self.assertEqual(mock_query.call_count, 2) |
- |
- def test_private_batch_get(self): |
- expected = { |
- "ConsumedCapacity": { |
- "CapacityUnits": 0.5, |
- "TableName": "users" |
- }, |
- 'Responses': { |
- 'users': [ |
- { |
- 'username': {'S': 'alice'}, |
- 'first_name': {'S': 'Alice'}, |
- 'last_name': {'S': 'Expert'}, |
- 'date_joined': {'N': '1366056680'}, |
- 'friend_count': {'N': '1'}, |
- 'friends': {'SS': ['jane']}, |
- }, |
- { |
- 'username': {'S': 'bob'}, |
- 'first_name': {'S': 'Bob'}, |
- 'last_name': {'S': 'Smith'}, |
- 'date_joined': {'N': '1366056888'}, |
- 'friend_count': {'N': '1'}, |
- 'friends': {'SS': ['johndoe']}, |
- }, |
- { |
- 'username': {'S': 'jane'}, |
- 'first_name': {'S': 'Jane'}, |
- 'last_name': {'S': 'Doe'}, |
- 'date_joined': {'N': '1366057777'}, |
- 'friend_count': {'N': '2'}, |
- 'friends': {'SS': ['alice', 'johndoe']}, |
- }, |
- ], |
- }, |
- "UnprocessedKeys": { |
- }, |
- } |
- |
- with mock.patch.object( |
- self.users.connection, |
- 'batch_get_item', |
- return_value=expected) as mock_batch_get: |
- results = self.users._batch_get(keys=[ |
- {'username': 'alice', 'friend_count': 1}, |
- {'username': 'bob', 'friend_count': 1}, |
- {'username': 'jane'}, |
- ]) |
- usernames = [res['username'] for res in results['results']] |
- self.assertEqual(usernames, ['alice', 'bob', 'jane']) |
- self.assertEqual(len(results['results']), 3) |
- self.assertEqual(results['last_key'], None) |
- self.assertEqual(results['unprocessed_keys'], []) |
- |
- mock_batch_get.assert_called_once_with(request_items={ |
- 'users': { |
- 'Keys': [ |
- { |
- 'username': {'S': 'alice'}, |
- 'friend_count': {'N': '1'} |
- }, |
- { |
- 'username': {'S': 'bob'}, |
- 'friend_count': {'N': '1'} |
- }, { |
- 'username': {'S': 'jane'}, |
- } |
- ] |
- } |
- }) |
- |
- # Now alter the expected. |
- del expected['Responses']['users'][2] |
- expected['UnprocessedKeys'] = { |
- 'Keys': [ |
- {'username': {'S': 'jane',}}, |
- ], |
- } |
- |
- with mock.patch.object( |
- self.users.connection, |
- 'batch_get_item', |
- return_value=expected) as mock_batch_get_2: |
- results = self.users._batch_get(keys=[ |
- {'username': 'alice', 'friend_count': 1}, |
- {'username': 'bob', 'friend_count': 1}, |
- {'username': 'jane'}, |
- ]) |
- usernames = [res['username'] for res in results['results']] |
- self.assertEqual(usernames, ['alice', 'bob']) |
- self.assertEqual(len(results['results']), 2) |
- self.assertEqual(results['last_key'], None) |
- self.assertEqual(results['unprocessed_keys'], [ |
- {'username': 'jane'} |
- ]) |
- |
- mock_batch_get_2.assert_called_once_with(request_items={ |
- 'users': { |
- 'Keys': [ |
- { |
- 'username': {'S': 'alice'}, |
- 'friend_count': {'N': '1'} |
- }, |
- { |
- 'username': {'S': 'bob'}, |
- 'friend_count': {'N': '1'} |
- }, { |
- 'username': {'S': 'jane'}, |
- } |
- ] |
- } |
- }) |
- |
- def test_private_batch_get_attributes(self): |
- # test if AttributesToGet parameter is passed to DynamoDB API |
- expected = { |
- "ConsumedCapacity": { |
- "CapacityUnits": 0.5, |
- "TableName": "users" |
- }, |
- 'Responses': { |
- 'users': [ |
- { |
- 'username': {'S': 'alice'}, |
- 'first_name': {'S': 'Alice'}, |
- }, |
- { |
- 'username': {'S': 'bob'}, |
- 'first_name': {'S': 'Bob'}, |
- }, |
- ], |
- }, |
- "UnprocessedKeys": {}, |
- } |
- |
- with mock.patch.object( |
- self.users.connection, |
- 'batch_get_item', |
- return_value=expected) as mock_batch_get_attr: |
- results = self.users._batch_get(keys=[ |
- {'username': 'alice'}, |
- {'username': 'bob'}, |
- ], attributes=['username', 'first_name']) |
- usernames = [res['username'] for res in results['results']] |
- first_names = [res['first_name'] for res in results['results']] |
- self.assertEqual(usernames, ['alice', 'bob']) |
- self.assertEqual(first_names, ['Alice', 'Bob']) |
- self.assertEqual(len(results['results']), 2) |
- self.assertEqual(results['last_key'], None) |
- self.assertEqual(results['unprocessed_keys'], []) |
- |
- mock_batch_get_attr.assert_called_once_with(request_items={ |
- 'users': { |
- 'Keys': [ { 'username': {'S': 'alice'} }, |
- { 'username': {'S': 'bob'} }, ], |
- 'AttributesToGet': ['username', 'first_name'], |
- }, |
- }) |
- |
- def test_batch_get(self): |
- items_1 = { |
- 'results': [ |
- Item(self.users, data={ |
- 'username': 'johndoe', |
- 'first_name': 'John', |
- 'last_name': 'Doe', |
- }), |
- Item(self.users, data={ |
- 'username': 'jane', |
- 'first_name': 'Jane', |
- 'last_name': 'Doe', |
- }), |
- ], |
- 'last_key': None, |
- 'unprocessed_keys': [ |
- 'zoeydoe', |
- ] |
- } |
- |
- results = self.users.batch_get(keys=[ |
- {'username': 'johndoe'}, |
- {'username': 'jane'}, |
- {'username': 'zoeydoe'}, |
- ]) |
- self.assertTrue(isinstance(results, BatchGetResultSet)) |
- self.assertEqual(len(results._results), 0) |
- self.assertEqual(results.the_callable, self.users._batch_get) |
- |
- with mock.patch.object( |
- results, |
- 'the_callable', |
- return_value=items_1) as mock_batch_get: |
- res_1 = next(results) |
- # Now it should be populated. |
- self.assertEqual(len(results._results), 2) |
- self.assertEqual(res_1['username'], 'johndoe') |
- res_2 = next(results) |
- self.assertEqual(res_2['username'], 'jane') |
- |
- self.assertEqual(mock_batch_get.call_count, 1) |
- self.assertEqual(results._keys_left, ['zoeydoe']) |
- |
- items_2 = { |
- 'results': [ |
- Item(self.users, data={ |
- 'username': 'zoeydoe', |
- 'first_name': 'Zoey', |
- 'last_name': 'Doe', |
- }), |
- ], |
- } |
- |
- with mock.patch.object( |
- results, |
- 'the_callable', |
- return_value=items_2) as mock_batch_get_2: |
- res_3 = next(results) |
- # New results should have been found. |
- self.assertEqual(len(results._results), 1) |
- self.assertEqual(res_3['username'], 'zoeydoe') |
- |
- self.assertRaises(StopIteration, results.next) |
- |
- self.assertEqual(mock_batch_get_2.call_count, 1) |
- self.assertEqual(results._keys_left, []) |