def setUp(self): from ddbmock.database.db import dynamodb from ddbmock.database.table import Table from ddbmock.database.key import PrimaryKey from ddbmock import main app = main({}) from webtest import TestApp self.app = TestApp(app) dynamodb.hard_reset() hash_key = PrimaryKey(TABLE_HK_NAME, TABLE_HK_TYPE) range_key = PrimaryKey(TABLE_RK_NAME, TABLE_RK_TYPE) self.t1 = Table(TABLE_NAME1, TABLE_RT, TABLE_WT, hash_key, range_key) self.t2 = Table(TABLE_NAME2, TABLE_RT, TABLE_WT, hash_key, None) dynamodb.data[TABLE_NAME1] = self.t1 dynamodb.data[TABLE_NAME2] = self.t2 self.t1.put(ITEM1, {}) self.t1.put(ITEM2, {}) self.t1.put(ITEM3, {}) self.t2.put(ITEM4, {}) self.t2.put(ITEM5, {})
class TestTable(unittest.TestCase): def setUp(self): from ddbmock.database.table import Table self.table = Table(NAME, RT, WT, None, None, status="ACTIVE") def tearDown(self): self.table = None def test_update_throughput_nominal(self): self.table.update_throughput(RT2, WT2) self.assertEqual(RT2, self.table.rt) self.assertEqual(WT2, self.table.wt) def test_increase_throughtput_max_100_percents(self): from ddbmock.errors import LimitExceededException # Try on RT self.assertRaisesRegexp(LimitExceededException, "ReadCapacityUnits .* at most 100", self.table.update_throughput, RT100, WT) self.assertEqual(RT, self.table.rt) self.assertEqual(WT, self.table.wt) # Try on WT self.assertRaisesRegexp(LimitExceededException, "WriteCapacityUnits .* at most 100", self.table.update_throughput, RT, WT100) self.assertEqual(RT, self.table.rt) self.assertEqual(WT, self.table.wt) @mock.patch("ddbmock.database.table.time") def test_decrease_max_once_a_day(self, m_time): from ddbmock.errors import LimitExceededException # 1st decrease 1 hour after creation (ok) m_time.time.return_value = CREATION + 1*3600 self.table.update_throughput(RT3, WT3) self.assertEqual(RT3, self.table.rt) self.assertEqual(WT3, self.table.wt) # bypass the timer self.table.status = "ACTIVE" # 2nd decrease 2 hour after creation (fail) m_time.time.return_value = CREATION + 2*3600 self.assertRaisesRegexp(LimitExceededException, "once .* day", self.table.update_throughput, RT4, WT4) self.assertEqual(RT3, self.table.rt) self.assertEqual(WT3, self.table.wt)
def setUp(self): from ddbmock.database.db import dynamodb from ddbmock.database.table import Table from ddbmock.database.key import PrimaryKey dynamodb.hard_reset() hash_key = PrimaryKey('hash_key', 'N') range_key = PrimaryKey('range_key', 'S') t1 = Table(TABLE_NAME1, 10, 10, hash_key, range_key) t2 = Table(TABLE_NAME2, 10, 10, hash_key, range_key) dynamodb.data[TABLE_NAME1] = t1 dynamodb.data[TABLE_NAME2] = t2
def setUp(self): from ddbmock.database.db import dynamodb from ddbmock.database.table import Table from ddbmock.database.key import PrimaryKey dynamodb.hard_reset() hash_key = PrimaryKey(TABLE_HK_NAME, TABLE_HK_TYPE) range_key = PrimaryKey(TABLE_RK_NAME, TABLE_RK_TYPE) t1 = Table(TABLE_NAME , TABLE_RT, TABLE_WT, hash_key, range_key, status="ACTIVE") t2 = Table(TABLE_NAME2, TABLE_RT, TABLE_WT, hash_key, range_key) dynamodb.data[TABLE_NAME] = t1 dynamodb.data[TABLE_NAME2] = t2
class TestUpdateItem(unittest.TestCase): def setUp(self): from ddbmock.database.db import dynamodb from ddbmock.database.table import Table from ddbmock.database.key import PrimaryKey from ddbmock import main app = main({}) from webtest import TestApp self.app = TestApp(app) dynamodb.hard_reset() hash_key = PrimaryKey(TABLE_HK_NAME, TABLE_HK_TYPE) range_key = PrimaryKey(TABLE_RK_NAME, TABLE_RK_TYPE) self.t1 = Table(TABLE_NAME, TABLE_RT, TABLE_WT, hash_key, range_key) dynamodb.data[TABLE_NAME] = self.t1 self.t1.put(ITEM, {}) def tearDown(self): from ddbmock.database.db import dynamodb dynamodb.hard_reset() def test_update_item_put_hr(self): request = { "TableName": TABLE_NAME, "Key": { "HashKeyElement": HK, "RangeKeyElement": RK, }, "AttributeUpdates": { 'relevant_data': { 'Value': RELEVANT_FIELD } }, } expected = {"ConsumedCapacityUnits": 1} # Protocol check res = self.app.post_json('/', request, headers=HEADERS, status=200) self.assertEqual(expected, json.loads(res.body)) self.assertEqual('application/x-amz-json-1.0; charset=UTF-8', res.headers['Content-Type']) # Live data check self.assertEqual(RELEVANT_FIELD, self.t1.store[HK_VALUE, RK_VALUE]['relevant_data'])
class TestGetItem(unittest.TestCase): def setUp(self): from ddbmock.database.db import dynamodb from ddbmock.database.table import Table from ddbmock.database.key import PrimaryKey from ddbmock.database.db import dynamodb from ddbmock import main app = main({}) from webtest import TestApp self.app = TestApp(app) dynamodb.hard_reset() hash_key = PrimaryKey(TABLE_HK_NAME, TABLE_HK_TYPE) range_key = PrimaryKey(TABLE_RK_NAME, TABLE_RK_TYPE) self.t1 = Table(TABLE_NAME, TABLE_RT, TABLE_WT, hash_key, range_key) dynamodb.data[TABLE_NAME] = self.t1 self.t1.put(ITEM, {}) def tearDown(self): from ddbmock.database.db import dynamodb dynamodb.hard_reset() def test_get_hr_attr_to_get(self): from ddbmock.database.db import dynamodb request = { "TableName": TABLE_NAME, "Key": { "HashKeyElement": HK, "RangeKeyElement": RK, }, "AttributesToGet": ["relevant_data"], } expected = { u'ConsumedCapacityUnits': 0.5, u'Item': { u'relevant_data': ITEM[u'relevant_data'], } } # Protocol check res = self.app.post_json('/', request, headers=HEADERS, status=200) self.assertEqual(expected, json.loads(res.body)) self.assertEqual('application/x-amz-json-1.0; charset=UTF-8', res.headers['Content-Type'])
def setUp(self): from ddbmock.database.db import dynamodb from ddbmock.database.table import Table from ddbmock.database.key import PrimaryKey dynamodb.hard_reset() hash_key = PrimaryKey(TABLE_HK_NAME, TABLE_HK_TYPE) range_key = PrimaryKey(TABLE_RK_NAME, TABLE_RK_TYPE) t1 = Table(TABLE_NAME, TABLE_RT, TABLE_WT, hash_key, range_key, status='ACTIVE') t1.put(ITEM1, {}) t1.put(ITEM2, {}) dynamodb.data[TABLE_NAME] = t1
class TestGetItem(unittest.TestCase): def setUp(self): from ddbmock.database.db import dynamodb from ddbmock.database.table import Table from ddbmock.database.key import PrimaryKey from ddbmock import main app = main({}) from webtest import TestApp self.app = TestApp(app) dynamodb.hard_reset() hash_key = PrimaryKey(TABLE_HK_NAME, TABLE_HK_TYPE) range_key = PrimaryKey(TABLE_RK_NAME, TABLE_RK_TYPE) self.t1 = Table(TABLE_NAME, TABLE_RT, TABLE_WT, hash_key, range_key) dynamodb.data[TABLE_NAME] = self.t1 self.t1.put(ITEM, {}) def tearDown(self): from ddbmock.database.db import dynamodb dynamodb.hard_reset() def test_get_hr_attr_to_get(self): request = { "TableName": TABLE_NAME, "Key": { "HashKeyElement": HK, "RangeKeyElement": RK, }, "AttributesToGet": ["relevant_data"], } expected = { u'ConsumedCapacityUnits': 0.5, u'Item': { u'relevant_data': ITEM[u'relevant_data'], } } # Protocol check res = self.app.post_json('/', request, headers=HEADERS, status=200) self.assertEqual(expected, json.loads(res.body)) self.assertEqual('application/x-amz-json-1.0; charset=UTF-8', res.headers['Content-Type'])
class TestDeleteItem(unittest.TestCase): def setUp(self): from ddbmock.database.db import dynamodb from ddbmock.database.table import Table from ddbmock.database.key import PrimaryKey from ddbmock.database.db import dynamodb from ddbmock import main app = main({}) from webtest import TestApp self.app = TestApp(app) dynamodb.hard_reset() hash_key = PrimaryKey(TABLE_HK_NAME, TABLE_HK_TYPE) range_key = PrimaryKey(TABLE_RK_NAME, TABLE_RK_TYPE) self.t1 = Table(TABLE_NAME, TABLE_RT, TABLE_WT, hash_key, range_key) dynamodb.data[TABLE_NAME] = self.t1 self.t1.put(ITEM, {}) def tearDown(self): from ddbmock.database.db import dynamodb dynamodb.hard_reset() def test_delete_item_hr(self): from ddbmock.database.db import dynamodb request = { "TableName": TABLE_NAME, "Key": { "HashKeyElement": HK, "RangeKeyElement": RK, }, } expected = { u"ConsumedCapacityUnits": 1, } # Protocol check res = self.app.post_json('/', request, headers=HEADERS, status=200) self.assertEqual(expected, json.loads(res.body)) self.assertEqual('application/x-amz-json-1.0; charset=UTF-8', res.headers['Content-Type']) # Live data check self.assertNotIn((HK_VALUE, RK_VALUE), self.t1.store)
def setUp(self): from ddbmock.database.db import dynamodb from ddbmock.database.table import Table from ddbmock.database.key import PrimaryKey dynamodb.hard_reset() hash_key = PrimaryKey(TABLE_HK_NAME, TABLE_HK_TYPE) range_key = PrimaryKey(TABLE_RK_NAME, TABLE_RK_TYPE) self.t1 = Table(TABLE_NAME, TABLE_RT, TABLE_WT, hash_key, range_key) self.t2 = Table(TABLE_NAME2, TABLE_RT, TABLE_WT, hash_key, None) dynamodb.data[TABLE_NAME] = self.t1 dynamodb.data[TABLE_NAME2] = self.t2 self.t1.put(cp(ITEM), {}) self.t2.put(cp(ITEM2), {})
def setUp(self): from ddbmock.database.db import dynamodb from ddbmock.database.table import Table from ddbmock.database.key import PrimaryKey dynamodb.hard_reset() hash_key = PrimaryKey(TABLE_HK_NAME, TABLE_HK_TYPE) range_key = PrimaryKey(TABLE_RK_NAME, TABLE_RK_TYPE) self.t1 = Table(TABLE_NAME, TABLE_RT, TABLE_WT, hash_key, range_key) dynamodb.data[TABLE_NAME] = self.t1 self.t1.put(ITEM1, {}) self.t1.put(ITEM2, {}) self.t1.put(ITEM3, {}) self.t1.put(ITEM4, {}) self.t1.put(ITEM5, {})
class TestUpdateItem(unittest.TestCase): def setUp(self): from ddbmock.database.db import dynamodb from ddbmock.database.table import Table from ddbmock.database.key import PrimaryKey import helpers self.app = helpers.makeTestApp() dynamodb.hard_reset() hash_key = PrimaryKey(TABLE_HK_NAME, TABLE_HK_TYPE) range_key = PrimaryKey(TABLE_RK_NAME, TABLE_RK_TYPE) self.t1 = Table(TABLE_NAME, TABLE_RT, TABLE_WT, hash_key, range_key) dynamodb.data[TABLE_NAME] = self.t1 self.t1.put(ITEM, {}) def tearDown(self): from ddbmock.database.db import dynamodb dynamodb.hard_reset() def test_update_item_put_hr(self): request = { "TableName": TABLE_NAME, "Key": { "HashKeyElement": HK, "RangeKeyElement": RK, }, "AttributeUpdates": {'relevant_data': {'Value': RELEVANT_FIELD}}, } expected = {"ConsumedCapacityUnits": 1} # Protocol check res = self.app.post_json('/', request, headers=HEADERS, status=200) self.assertEqual(expected, json.loads(res.body)) self.assertEqual('application/x-amz-json-1.0; charset=UTF-8', res.headers['Content-Type']) # Live data check self.assertEqual(RELEVANT_FIELD, self.t1.store[HK_VALUE, RK_VALUE]['relevant_data'])
def setUp(self): from ddbmock.database.db import dynamodb from ddbmock.database.table import Table from ddbmock.database.key import PrimaryKey from ddbmock import main app = main({}) from webtest import TestApp self.app = TestApp(app) dynamodb.hard_reset() hash_key = PrimaryKey('hash_key', 'N') range_key = PrimaryKey('range_key', 'S') t1 = Table(TABLE_NAME1, 10, 10, hash_key, range_key) t2 = Table(TABLE_NAME2, 10, 10, hash_key, range_key) dynamodb.data[TABLE_NAME1] = t1 dynamodb.data[TABLE_NAME2] = t2
def setUp(self): from ddbmock.database.db import dynamodb from ddbmock.database.table import Table from ddbmock.database.key import PrimaryKey import helpers self.app = helpers.makeTestApp() dynamodb.hard_reset() hash_key = PrimaryKey(TABLE_HK_NAME, TABLE_HK_TYPE) range_key = PrimaryKey(TABLE_RK_NAME, TABLE_RK_TYPE) self.t1 = Table(TABLE_NAME1, TABLE_RT, TABLE_WT, hash_key, range_key) self.t2 = Table(TABLE_NAME2, TABLE_RT, TABLE_WT, hash_key, None) dynamodb.data[TABLE_NAME1] = self.t1 dynamodb.data[TABLE_NAME2] = self.t2 self.t1.put(ITEM1, {}) self.t2.put(ITEM4, {})
def setUp(self): from ddbmock.database.db import dynamodb from ddbmock.database.table import Table from ddbmock.database.key import PrimaryKey from ddbmock import main app = main({}) from webtest import TestApp self.app = TestApp(app) dynamodb.hard_reset() hash_key = PrimaryKey(TABLE_HK_NAME, TABLE_HK_TYPE) range_key = PrimaryKey(TABLE_RK_NAME, TABLE_RK_TYPE) self.t1 = Table(TABLE_NAME, TABLE_RT, TABLE_WT, hash_key, range_key) dynamodb.data[TABLE_NAME] = self.t1
def setUp(self, m_time): from ddbmock.database.db import dynamodb from ddbmock.database.table import Table from ddbmock.database.key import PrimaryKey from ddbmock import main app = main({}) from webtest import TestApp self.app = TestApp(app) m_time.time.return_value = NOW dynamodb.hard_reset() hash_key = PrimaryKey(TABLE_HK_NAME, TABLE_HK_TYPE) range_key = PrimaryKey(TABLE_RK_NAME, TABLE_RK_TYPE) t1 = Table(TABLE_NAME, TABLE_RT, TABLE_WT, hash_key, range_key, status='ACTIVE') dynamodb.data[TABLE_NAME] = t1
class TestBatchWriteItem(unittest.TestCase): def setUp(self): from ddbmock.database.db import dynamodb from ddbmock.database.table import Table from ddbmock.database.key import PrimaryKey from ddbmock import main app = main({}) from webtest import TestApp self.app = TestApp(app) dynamodb.hard_reset() hash_key = PrimaryKey(TABLE_HK_NAME, TABLE_HK_TYPE) range_key = PrimaryKey(TABLE_RK_NAME, TABLE_RK_TYPE) self.t1 = Table(TABLE_NAME1, TABLE_RT, TABLE_WT, hash_key, range_key) self.t2 = Table(TABLE_NAME2, TABLE_RT, TABLE_WT, hash_key, None) dynamodb.data[TABLE_NAME1] = self.t1 dynamodb.data[TABLE_NAME2] = self.t2 self.t1.put(ITEM1, {}) self.t2.put(ITEM4, {}) def tearDown(self): from ddbmock.database.db import dynamodb dynamodb.hard_reset() def test_batch_write_item(self): request = { u"RequestItems": { TABLE_NAME1: [ { u"DeleteRequest": { u"Key": { u"HashKeyElement": {TABLE_HK_TYPE: HK_VALUE1}, u"RangeKeyElement": {TABLE_RK_TYPE: RK_VALUE1}, }, }, }, {u"PutRequest": {u"Item": ITEM2}}, {u"PutRequest": {u"Item": ITEM3}}, ], TABLE_NAME2: [ { u"DeleteRequest": { u"Key": { u"HashKeyElement": {TABLE_HK_TYPE: HK_VALUE2}, }, }, }, {u"PutRequest": {u"Item":ITEM5}}, ], } } expected = { "Responses": { TABLE_NAME1: { "ConsumedCapacityUnits": 3 }, TABLE_NAME2: { "ConsumedCapacityUnits": 2 } } } # Protocol check res = self.app.post_json('/', request, headers=HEADERS, status=200) self.assertEqual(expected, json.loads(res.body)) self.assertEqual('application/x-amz-json-1.0; charset=UTF-8', res.headers['Content-Type'])
class TestBatchGetItem(unittest.TestCase): def setUp(self): from ddbmock.database.db import dynamodb from ddbmock.database.table import Table from ddbmock.database.key import PrimaryKey dynamodb.hard_reset() hash_key = PrimaryKey(TABLE_HK_NAME, TABLE_HK_TYPE) range_key = PrimaryKey(TABLE_RK_NAME, TABLE_RK_TYPE) self.t1 = Table(TABLE_NAME1, TABLE_RT, TABLE_WT, hash_key, range_key) self.t2 = Table(TABLE_NAME2, TABLE_RT, TABLE_WT, hash_key, None) dynamodb.data[TABLE_NAME1] = self.t1 dynamodb.data[TABLE_NAME2] = self.t2 self.t1.put(ITEM1, {}) self.t1.put(ITEM2, {}) self.t1.put(ITEM3, {}) self.t2.put(ITEM4, {}) self.t2.put(ITEM5, {}) self.t2.put(ITEM_BIG, {}) def tearDown(self): from ddbmock.database.db import dynamodb from ddbmock import clean_boto_patch dynamodb.hard_reset() clean_boto_patch() def test_batch_get_item_nominal(self): from ddbmock import connect_boto_patch from ddbmock.database.db import dynamodb db = connect_boto_patch() expected = { "Responses": { "Table-HR": { "Items": [ITEM1, ITEM2], "ConsumedCapacityUnits": 1.0 }, "Table-H": { "Items": [ITEM5, ITEM_BIG], "ConsumedCapacityUnits": 1.5 } } } ret = db.layer1.batch_get_item({ TABLE_NAME1: { u"Keys": [ {u"HashKeyElement": {TABLE_HK_TYPE: HK_VALUE1}, u"RangeKeyElement": {TABLE_RK_TYPE: RK_VALUE1}}, {u"HashKeyElement": {TABLE_HK_TYPE: HK_VALUE1}, u"RangeKeyElement": {TABLE_RK_TYPE: RK_VALUE2}}, ], }, TABLE_NAME2: { u"Keys": [ {u"HashKeyElement": {TABLE_HK_TYPE: HK_VALUE3}}, {u"HashKeyElement": {TABLE_HK_TYPE: HK_VALUE1}}, {u"HashKeyElement": {TABLE_HK_TYPE: u"404"}}, ], }, }) self.assertEqual(expected, ret) def test_batch_get_item_filter_one(self): from ddbmock import connect_boto_patch from ddbmock.database.db import dynamodb db = connect_boto_patch() expected = { "Responses": { "Table-HR": { "Items": [ {"relevant_data": {"S": "tata"}}, {"relevant_data": {"S": "tata"}}, ], "ConsumedCapacityUnits": 1.0 }, "Table-H": { "Items": [ {"relevant_data": {"S": "tutu"}, "hash_key": {"N": "789"}, "range_key": {"S": "Waldo-5"}}, ], "ConsumedCapacityUnits": 0.5 } } } ret = db.layer1.batch_get_item({ TABLE_NAME1: { u"Keys": [ {u"HashKeyElement": {TABLE_HK_TYPE: HK_VALUE1}, u"RangeKeyElement": {TABLE_RK_TYPE: RK_VALUE1}}, {u"HashKeyElement": {TABLE_HK_TYPE: HK_VALUE1}, u"RangeKeyElement": {TABLE_RK_TYPE: RK_VALUE1}}, ], u"AttributesToGet": [u"relevant_data"], }, TABLE_NAME2: { u"Keys": [ {u"HashKeyElement": {TABLE_HK_TYPE: HK_VALUE3}}, ], }, }) self.assertEqual(expected, ret) def test_batch_get_item_table_404(self): from ddbmock import connect_boto_patch from ddbmock.database.db import dynamodb from boto.exception import BotoServerError db = connect_boto_patch() self.assertRaises(BotoServerError, db.layer1.batch_get_item, { TABLE_NAME_404: { u"Keys": [ {u"HashKeyElement": {TABLE_HK_TYPE: HK_VALUE1}, u"RangeKeyElement": {TABLE_RK_TYPE: RK_VALUE1}}, {u"HashKeyElement": {TABLE_HK_TYPE: HK_VALUE1}, u"RangeKeyElement": {TABLE_RK_TYPE: RK_VALUE1}}, ], }, TABLE_NAME2: { u"Keys": [ {u"HashKeyElement": {TABLE_HK_TYPE: HK_VALUE3}}, ], }, }) def test_batch_get_item_bad_key(self): from ddbmock import connect_boto_patch from ddbmock.database.db import dynamodb from boto.dynamodb.exceptions import DynamoDBValidationError db = connect_boto_patch() self.assertRaises(DynamoDBValidationError, db.layer1.batch_get_item, { TABLE_NAME1: { u"Keys": [ {u"HashKeyElement": {TABLE_HK_TYPE: HK_VALUE1}, u"RangeKeyElement": {TABLE_RK_TYPE: RK_VALUE1}}, {u"HashKeyElement": {TABLE_HK_TYPE: HK_VALUE1}}, ], }, TABLE_NAME2: { u"Keys": [ {u"HashKeyElement": {TABLE_HK_TYPE: HK_VALUE3}}, ], }, }) self.assertRaises(DynamoDBValidationError, db.layer1.batch_get_item, { TABLE_NAME1: { u"Keys": [ {u"HashKeyElement": {TABLE_HK_TYPE: HK_VALUE1}, u"RangeKeyElement": {TABLE_RK_TYPE: RK_VALUE1}}, {u"HashKeyElement": {TABLE_HK_TYPE: HK_VALUE1}, u"RangeKeyElement": {TABLE_RK_TYPE: RK_VALUE1}}, ], }, TABLE_NAME2: { u"Keys": [ {u"HashKeyElement": {TABLE_HK_TYPE: HK_VALUE3}, u"RangeKeyElement": {TABLE_RK_TYPE: RK_VALUE1}}, ], }, })
class TestQuery(unittest.TestCase): def setUp(self): from ddbmock.database.db import dynamodb from ddbmock.database.table import Table from ddbmock.database.key import PrimaryKey from ddbmock import main app = main({}) from webtest import TestApp self.app = TestApp(app) dynamodb.hard_reset() hash_key = PrimaryKey(TABLE_HK_NAME, TABLE_HK_TYPE) range_key = PrimaryKey(TABLE_RK_NAME, TABLE_RK_TYPE) self.t1 = Table(TABLE_NAME, TABLE_RT, TABLE_WT, hash_key, range_key) dynamodb.data[TABLE_NAME] = self.t1 self.t1.put(ITEM1, {}) self.t1.put(ITEM2, {}) self.t1.put(ITEM3, {}) self.t1.put(ITEM4, {}) self.t1.put(ITEM5, {}) def tearDown(self): from ddbmock.database.db import dynamodb dynamodb.hard_reset() def test_query_condition_filter_fields(self): request = { "TableName": TABLE_NAME, "HashKeyValue": {TABLE_HK_TYPE: HK_VALUE}, "RangeKeyCondition": { "AttributeValueList": [{"S":"Waldo-2"}], "ComparisonOperator": "GT", }, "AttributesToGet": [u'relevant_data'], } expected = { u"Count": 3, u"Items": [ {u"relevant_data": {u"S": u"titi"}}, {u"relevant_data": {u"S": u"toto"}}, {u"relevant_data": {u"S": u"tutu"}}, ], u"ConsumedCapacityUnits": 0.5, } # Protocol check res = self.app.post_json('/', request, headers=HEADERS, status=200) self.assertEqual(expected, json.loads(res.body)) self.assertEqual('application/x-amz-json-1.0; charset=UTF-8', res.headers['Content-Type']) def test_query_count_and_attrs_to_get_fails(self): request = { "TableName": TABLE_NAME, "HashKeyValue": {TABLE_HK_TYPE: HK_VALUE}, "RangeKeyCondition": { "AttributeValueList": [{"S":"Waldo-2"}], "ComparisonOperator": "GT", }, "AttributesToGet": [u'relevant_data'], "Count": True, } expected = { u'__type': u'com.amazonaws.dynamodb.v20111205#ValidationException', u'message': u'Can filter fields when only count is requested' } # Protocol check res = self.app.post_json('/', request, headers=HEADERS, status=400) self.assertEqual(expected, json.loads(res.body)) self.assertEqual('application/x-amz-json-1.0; charset=UTF-8', res.headers['Content-Type']) # Regression test for #9 def test_query_all_404(self): request = { "TableName": TABLE_NAME, "HashKeyValue": {TABLE_HK_TYPE: HK_VALUE_404}, } expected = { u"Count": 0, u'Items': [], u"ConsumedCapacityUnits": 0.5, } # Protocol check res = self.app.post_json('/', request, headers=HEADERS, status=200) self.assertEqual(expected, json.loads(res.body)) self.assertEqual('application/x-amz-json-1.0; charset=UTF-8', res.headers['Content-Type'])
class TestDeleteItem(unittest.TestCase): def setUp(self): from ddbmock.database.db import dynamodb from ddbmock.database.table import Table from ddbmock.database.key import PrimaryKey dynamodb.hard_reset() hash_key = PrimaryKey(TABLE_HK_NAME, TABLE_HK_TYPE) range_key = PrimaryKey(TABLE_RK_NAME, TABLE_RK_TYPE) self.t1 = Table(TABLE_NAME, TABLE_RT, TABLE_WT, hash_key, range_key) self.t2 = Table(TABLE_NAME2, TABLE_RT, TABLE_WT, hash_key, None) dynamodb.data[TABLE_NAME] = self.t1 dynamodb.data[TABLE_NAME2] = self.t2 self.t1.put(ITEM, {}) self.t2.put(ITEM2, {}) self.t2.put(ITEM_BIG, {}) def tearDown(self): from ddbmock.database.db import dynamodb from ddbmock import clean_boto_patch dynamodb.hard_reset() clean_boto_patch() def test_delete_item_hr(self): from ddbmock import connect_boto_patch from ddbmock.database.db import dynamodb db = connect_boto_patch() key = { u"HashKeyElement": {TABLE_HK_TYPE: HK_VALUE}, u"RangeKeyElement": {TABLE_RK_TYPE: RK_VALUE}, } self.assertEqual({ u'ConsumedCapacityUnits': 1, }, db.layer1.delete_item(TABLE_NAME, key), ) self.assertNotIn((HK_VALUE, RK_VALUE), self.t1.store) def test_delete_item_hr_404(self): # same behavior as found from ddbmock import connect_boto_patch from ddbmock.database.db import dynamodb db = connect_boto_patch() key = { u"HashKeyElement": {TABLE_HK_TYPE: HK_VALUE_404}, u"RangeKeyElement": {TABLE_RK_TYPE: RK_VALUE}, } self.assertEqual({ u'ConsumedCapacityUnits': 1, }, db.layer1.delete_item(TABLE_NAME, key), ) self.assertNotIn((HK_VALUE_404, RK_VALUE), self.t1.store) def test_delete_item_hr_old(self): from ddbmock import connect_boto_patch from ddbmock.database.db import dynamodb db = connect_boto_patch() expected = { u'ConsumedCapacityUnits': 1, u'Attributes': ITEM, } key = { u"HashKeyElement": {TABLE_HK_TYPE: HK_VALUE}, u"RangeKeyElement": {TABLE_RK_TYPE: RK_VALUE}, } self.assertEqual( expected, db.layer1.delete_item(TABLE_NAME, key, return_values=u'ALL_OLD'), ) self.assertNotIn((HK_VALUE, RK_VALUE), self.t1.store) def test_delete_item_h(self): from ddbmock import connect_boto_patch from ddbmock.database.db import dynamodb db = connect_boto_patch() key = { u"HashKeyElement": {TABLE_HK_TYPE: HK_VALUE}, } self.assertEqual({ u'ConsumedCapacityUnits': 1, }, db.layer1.delete_item(TABLE_NAME2, key), ) self.assertNotIn((HK_VALUE, False), self.t2.store) def test_delete_item_h_big(self): from ddbmock import connect_boto_patch from ddbmock.database.db import dynamodb db = connect_boto_patch() key = { u"HashKeyElement": {TABLE_HK_TYPE: HK_VALUE2}, } self.assertEqual({ u'ConsumedCapacityUnits': 2, }, db.layer1.delete_item(TABLE_NAME2, key), ) self.assertNotIn((HK_VALUE2, False), self.t2.store) def test_delete_item_h_old(self): from ddbmock import connect_boto_patch from ddbmock.database.db import dynamodb db = connect_boto_patch() expected = { u'ConsumedCapacityUnits': 1, u'Attributes': ITEM2, } key = { u"HashKeyElement": {TABLE_HK_TYPE: HK_VALUE}, } self.assertEqual( expected, db.layer1.delete_item(TABLE_NAME2, key, return_values=u'ALL_OLD'), ) self.assertNotIn((HK_VALUE, False), self.t1.store) def test_delete_item_hr_missing_r(self): from ddbmock import connect_boto_patch from ddbmock.database.db import dynamodb from boto.dynamodb.exceptions import DynamoDBValidationError db = connect_boto_patch() key = { u"HashKeyElement": {TABLE_HK_TYPE: HK_VALUE}, } self.assertRaises(DynamoDBValidationError, db.layer1.delete_item, TABLE_NAME, key) def test_delete_item_h_missing_h(self): from ddbmock import connect_boto_patch from ddbmock.database.db import dynamodb from boto.dynamodb.exceptions import DynamoDBValidationError db = connect_boto_patch() key = {} self.assertRaises(DynamoDBValidationError, db.layer1.delete_item, TABLE_NAME2, key) def test_delete_item_h_expect_field_value_ok(self): from ddbmock import connect_boto_patch from ddbmock.database.db import dynamodb from boto.exception import DynamoDBResponseError db = connect_boto_patch() ddb_expected = { u'relevant_data': { u'Exists': True, u'Value': {u'B': u'THVkaWEgaXMgdGhlIGJlc3QgY29tcGFueSBldmVyIQ=='} } } key = { u"HashKeyElement": {TABLE_HK_TYPE: HK_VALUE}, u"RangeKeyElement": {TABLE_RK_TYPE: RK_VALUE}, } db.layer1.delete_item(TABLE_NAME, key, expected=ddb_expected) self.assertNotIn((HK_VALUE, False), self.t1.store) def test_delete_item_h_expect_field_value_fail(self): from ddbmock import connect_boto_patch from ddbmock.database.db import dynamodb from boto.dynamodb.exceptions import DynamoDBConditionalCheckFailedError db = connect_boto_patch() ddb_expected = { u'relevant_data_et_bah_non': { u'Exists': True, u'Value': {u'B': u'THVkaWEgaXMgdGhlIGJlc3QgY29tcGFueSBldmVyIQ=='} } } key = { u"HashKeyElement": {TABLE_HK_TYPE: HK_VALUE}, u"RangeKeyElement": {TABLE_RK_TYPE: RK_VALUE}, } self.assertRaisesRegexp(DynamoDBConditionalCheckFailedError, 'ConditionalCheckFailedException', db.layer1.delete_item, TABLE_NAME, key, expected=ddb_expected ) self.assertEqual(ITEM, self.t1.store[HK_VALUE, RK_VALUE])
class TestDeleteItem(unittest.TestCase): def setUp(self): from ddbmock.database.db import dynamodb from ddbmock.database.table import Table from ddbmock.database.key import PrimaryKey dynamodb.hard_reset() hash_key = PrimaryKey(TABLE_HK_NAME, TABLE_HK_TYPE) range_key = PrimaryKey(TABLE_RK_NAME, TABLE_RK_TYPE) self.t1 = Table(TABLE_NAME, TABLE_RT, TABLE_WT, hash_key, range_key) self.t2 = Table(TABLE_NAME2, TABLE_RT, TABLE_WT, hash_key, None) dynamodb.data[TABLE_NAME] = self.t1 dynamodb.data[TABLE_NAME2] = self.t2 self.t1.put(ITEM, {}) self.t2.put(ITEM2, {}) self.t2.put(ITEM_BIG, {}) def tearDown(self): from ddbmock.database.db import dynamodb from ddbmock import clean_boto_patch dynamodb.hard_reset() clean_boto_patch() def test_delete_item_hr(self): from ddbmock import connect_boto_patch from ddbmock.database.db import dynamodb db = connect_boto_patch() key = { u"HashKeyElement": { TABLE_HK_TYPE: HK_VALUE }, u"RangeKeyElement": { TABLE_RK_TYPE: RK_VALUE }, } self.assertEqual( { u'ConsumedCapacityUnits': 1, }, db.layer1.delete_item(TABLE_NAME, key), ) self.assertNotIn((HK_VALUE, RK_VALUE), self.t1.store) def test_delete_item_hr_404(self): # same behavior as found from ddbmock import connect_boto_patch from ddbmock.database.db import dynamodb db = connect_boto_patch() key = { u"HashKeyElement": { TABLE_HK_TYPE: HK_VALUE_404 }, u"RangeKeyElement": { TABLE_RK_TYPE: RK_VALUE }, } self.assertEqual( { u'ConsumedCapacityUnits': 1, }, db.layer1.delete_item(TABLE_NAME, key), ) self.assertNotIn((HK_VALUE_404, RK_VALUE), self.t1.store) def test_delete_item_hr_old(self): from ddbmock import connect_boto_patch from ddbmock.database.db import dynamodb db = connect_boto_patch() expected = { u'ConsumedCapacityUnits': 1, u'Attributes': ITEM, } key = { u"HashKeyElement": { TABLE_HK_TYPE: HK_VALUE }, u"RangeKeyElement": { TABLE_RK_TYPE: RK_VALUE }, } self.assertEqual( expected, db.layer1.delete_item(TABLE_NAME, key, return_values=u'ALL_OLD'), ) self.assertNotIn((HK_VALUE, RK_VALUE), self.t1.store) def test_delete_item_h(self): from ddbmock import connect_boto_patch from ddbmock.database.db import dynamodb db = connect_boto_patch() key = { u"HashKeyElement": { TABLE_HK_TYPE: HK_VALUE }, } self.assertEqual( { u'ConsumedCapacityUnits': 1, }, db.layer1.delete_item(TABLE_NAME2, key), ) self.assertNotIn((HK_VALUE, False), self.t2.store) def test_delete_item_h_big(self): from ddbmock import connect_boto_patch from ddbmock.database.db import dynamodb db = connect_boto_patch() key = { u"HashKeyElement": { TABLE_HK_TYPE: HK_VALUE2 }, } self.assertEqual( { u'ConsumedCapacityUnits': 2, }, db.layer1.delete_item(TABLE_NAME2, key), ) self.assertNotIn((HK_VALUE2, False), self.t2.store) def test_delete_item_h_old(self): from ddbmock import connect_boto_patch from ddbmock.database.db import dynamodb db = connect_boto_patch() expected = { u'ConsumedCapacityUnits': 1, u'Attributes': ITEM2, } key = { u"HashKeyElement": { TABLE_HK_TYPE: HK_VALUE }, } self.assertEqual( expected, db.layer1.delete_item(TABLE_NAME2, key, return_values=u'ALL_OLD'), ) self.assertNotIn((HK_VALUE, False), self.t1.store) def test_delete_item_hr_missing_r(self): from ddbmock import connect_boto_patch from ddbmock.database.db import dynamodb from boto.dynamodb.exceptions import DynamoDBValidationError db = connect_boto_patch() key = { u"HashKeyElement": { TABLE_HK_TYPE: HK_VALUE }, } self.assertRaises(DynamoDBValidationError, db.layer1.delete_item, TABLE_NAME, key) def test_delete_item_h_missing_h(self): from ddbmock import connect_boto_patch from ddbmock.database.db import dynamodb from boto.dynamodb.exceptions import DynamoDBValidationError db = connect_boto_patch() key = {} self.assertRaises(DynamoDBValidationError, db.layer1.delete_item, TABLE_NAME2, key) def test_delete_item_h_expect_field_value_ok(self): from ddbmock import connect_boto_patch from ddbmock.database.db import dynamodb from boto.exception import DynamoDBResponseError db = connect_boto_patch() ddb_expected = { u'relevant_data': { u'Exists': True, u'Value': { u'B': u'THVkaWEgaXMgdGhlIGJlc3QgY29tcGFueSBldmVyIQ==' } } } key = { u"HashKeyElement": { TABLE_HK_TYPE: HK_VALUE }, u"RangeKeyElement": { TABLE_RK_TYPE: RK_VALUE }, } db.layer1.delete_item(TABLE_NAME, key, expected=ddb_expected) self.assertNotIn((HK_VALUE, False), self.t1.store) def test_delete_item_h_expect_field_value_fail(self): from ddbmock import connect_boto_patch from ddbmock.database.db import dynamodb from boto.dynamodb.exceptions import DynamoDBConditionalCheckFailedError db = connect_boto_patch() ddb_expected = { u'relevant_data_et_bah_non': { u'Exists': True, u'Value': { u'B': u'THVkaWEgaXMgdGhlIGJlc3QgY29tcGFueSBldmVyIQ==' } } } key = { u"HashKeyElement": { TABLE_HK_TYPE: HK_VALUE }, u"RangeKeyElement": { TABLE_RK_TYPE: RK_VALUE }, } self.assertRaisesRegexp(DynamoDBConditionalCheckFailedError, 'ConditionalCheckFailedException', db.layer1.delete_item, TABLE_NAME, key, expected=ddb_expected) self.assertEqual(ITEM, self.t1.store[HK_VALUE, RK_VALUE])
class TestBatchGetItem(unittest.TestCase): def setUp(self): from ddbmock.database.db import dynamodb from ddbmock.database.table import Table from ddbmock.database.key import PrimaryKey import helpers self.app = helpers.makeTestApp() dynamodb.hard_reset() hash_key = PrimaryKey(TABLE_HK_NAME, TABLE_HK_TYPE) range_key = PrimaryKey(TABLE_RK_NAME, TABLE_RK_TYPE) self.t1 = Table(TABLE_NAME1, TABLE_RT, TABLE_WT, hash_key, range_key) self.t2 = Table(TABLE_NAME2, TABLE_RT, TABLE_WT, hash_key, None) dynamodb.data[TABLE_NAME1] = self.t1 dynamodb.data[TABLE_NAME2] = self.t2 self.t1.put(ITEM1, {}) self.t1.put(ITEM2, {}) self.t1.put(ITEM3, {}) self.t2.put(ITEM4, {}) self.t2.put(ITEM5, {}) def tearDown(self): from ddbmock.database.db import dynamodb dynamodb.hard_reset() def test_batch_get_item_filter_one(self): request = { u"RequestItems": { TABLE_NAME1: { u"Keys": [ {u"HashKeyElement": {TABLE_HK_TYPE: HK_VALUE1}, u"RangeKeyElement": {TABLE_RK_TYPE: RK_VALUE1}, }, {u"HashKeyElement": {TABLE_HK_TYPE: HK_VALUE1}, u"RangeKeyElement": {TABLE_RK_TYPE: RK_VALUE1} }, ], u"AttributesToGet": [u"relevant_data"], }, TABLE_NAME2: { u"Keys": [ {u"HashKeyElement": {TABLE_HK_TYPE: HK_VALUE3}}, ], }, } } expected = { "Responses": { "Table-HR": { "Items": [ {"relevant_data": {"S": "tata"}}, {"relevant_data": {"S": "tata"}}, ], "ConsumedCapacityUnits": 1.0 }, "Table-H": { "Items": [ {"relevant_data": {"S": "tutu"}, "hash_key": {"N": "789"}, "range_key": {"S": "Waldo-5"} }, ], "ConsumedCapacityUnits": 0.5 } } } # Protocol check res = self.app.post_json('/', request, headers=HEADERS, status=200) self.assertEqual(expected, json.loads(res.body)) self.assertEqual('application/x-amz-json-1.0; charset=UTF-8', res.headers['Content-Type']) def test_batch_get_item_filter_one_consistent(self): request = { u"RequestItems": { TABLE_NAME1: { u"Keys": [ {u"HashKeyElement": {TABLE_HK_TYPE: HK_VALUE1}, u"RangeKeyElement": {TABLE_RK_TYPE: RK_VALUE1} }, {u"HashKeyElement": {TABLE_HK_TYPE: HK_VALUE1}, u"RangeKeyElement": {TABLE_RK_TYPE: RK_VALUE1} }, ], u"AttributesToGet": [u"relevant_data"], u"ConsistentRead": True, }, TABLE_NAME2: { u"Keys": [ {u"HashKeyElement": {TABLE_HK_TYPE: HK_VALUE3}}, ], }, } } expected = { "Responses": { "Table-HR": { "Items": [ {"relevant_data": {"S": "tata"}}, {"relevant_data": {"S": "tata"}}, ], "ConsumedCapacityUnits": 2.0 }, "Table-H": { "Items": [ {"relevant_data": {"S": "tutu"}, "hash_key": {"N": "789"}, "range_key": {"S": "Waldo-5"} }, ], "ConsumedCapacityUnits": 0.5 } } } # Protocol check res = self.app.post_json('/', request, headers=HEADERS, status=200) self.assertEqual(expected, json.loads(res.body)) self.assertEqual('application/x-amz-json-1.0; charset=UTF-8', res.headers['Content-Type'])
class TestUpdateItem(unittest.TestCase): def setUp(self): from ddbmock.database.db import dynamodb from ddbmock.database.table import Table from ddbmock.database.key import PrimaryKey dynamodb.hard_reset() hash_key = PrimaryKey(TABLE_HK_NAME, TABLE_HK_TYPE) range_key = PrimaryKey(TABLE_RK_NAME, TABLE_RK_TYPE) self.t1 = Table(TABLE_NAME, TABLE_RT, TABLE_WT, hash_key, range_key) self.t2 = Table(TABLE_NAME2, TABLE_RT, TABLE_WT, hash_key, None) dynamodb.data[TABLE_NAME] = self.t1 dynamodb.data[TABLE_NAME2] = self.t2 self.t1.put(cp(ITEM), {}) self.t2.put(cp(ITEM2), {}) def tearDown(self): from ddbmock.database.db import dynamodb from ddbmock import clean_boto_patch dynamodb.hard_reset() clean_boto_patch() def test_update_item_put_hr(self): from ddbmock import connect_boto_patch db = connect_boto_patch() key = {u"HashKeyElement": {TABLE_HK_TYPE: HK_VALUE}, u"RangeKeyElement": {TABLE_RK_TYPE: RK_VALUE}} key2 = {u"HashKeyElement": {TABLE_HK_TYPE: HK_VALUE2}, u"RangeKeyElement": {TABLE_RK_TYPE: RK_VALUE}} # use PUT as default action, champs existant db.layer1.update_item( TABLE_NAME, key, {"relevant_data": {"Value": RELEVANT_FIELD}} # Move type from 'B' to 'S' ) self.assertEqual(RELEVANT_FIELD, self.t1.store[HK_VALUE, RK_VALUE]["relevant_data"]) # PUT explicite, champ non existant db.layer1.update_item(TABLE_NAME, key, {"irelevant_data": {"Action": "PUT", "Value": IRELEVANT_FIELD}}) self.assertEqual(RELEVANT_FIELD, self.t1.store[HK_VALUE, RK_VALUE]["relevant_data"]) self.assertEqual(IRELEVANT_FIELD, self.t1.store[HK_VALUE, RK_VALUE]["irelevant_data"]) # PUT explicite, item non existant(full item creation) db.layer1.update_item(TABLE_NAME, key2, {"relevant_data": {"Action": "PUT", "Value": RELEVANT_FIELD}}) self.assertEqual({TABLE_HK_TYPE: HK_VALUE2}, self.t1.store[HK_VALUE2, RK_VALUE][TABLE_HK_NAME]) self.assertEqual({TABLE_RK_TYPE: RK_VALUE}, self.t1.store[HK_VALUE2, RK_VALUE][TABLE_RK_NAME]) self.assertEqual(RELEVANT_FIELD, self.t1.store[HK_VALUE2, RK_VALUE]["relevant_data"]) def test_update_item_put_h(self): from ddbmock import connect_boto_patch db = connect_boto_patch() key = {u"HashKeyElement": {TABLE_HK_TYPE: HK_VALUE}} key2 = {u"HashKeyElement": {TABLE_HK_TYPE: HK_VALUE2}} # use PUT as default action, champs existant db.layer1.update_item( TABLE_NAME2, key, {"relevant_data": {"Value": RELEVANT_FIELD}} # Move type from 'B' to 'S' ) self.assertEqual(RELEVANT_FIELD, self.t2.store[HK_VALUE, False]["relevant_data"]) # PUT explicite, champ non existant db.layer1.update_item(TABLE_NAME2, key, {"irelevant_data": {"Action": "PUT", "Value": IRELEVANT_FIELD}}) self.assertEqual(RELEVANT_FIELD, self.t2.store[HK_VALUE, False]["relevant_data"]) self.assertEqual(IRELEVANT_FIELD, self.t2.store[HK_VALUE, False]["irelevant_data"]) # PUT explicite, item non existant(full item creation) db.layer1.update_item(TABLE_NAME2, key2, {"relevant_data": {"Action": "PUT", "Value": RELEVANT_FIELD}}) self.assertEqual({TABLE_HK_TYPE: HK_VALUE2}, self.t2.store[HK_VALUE2, False][TABLE_HK_NAME]) self.assertEqual(RELEVANT_FIELD, self.t2.store[HK_VALUE2, False]["relevant_data"]) def test_put_check_throughput_max_old_new(self): from ddbmock import connect_boto_patch from ddbmock.database.db import dynamodb db = connect_boto_patch() key = {u"HashKeyElement": {TABLE_HK_TYPE: HK_VALUE}} self.assertEqual( {u"ConsumedCapacityUnits": 1}, db.layer1.update_item(TABLE_NAME2, key, {FIELD_NAME: {"Action": "PUT", "Value": FIELD_SMALL}}), ) self.assertEqual( {u"ConsumedCapacityUnits": 2}, db.layer1.update_item(TABLE_NAME2, key, {FIELD_NAME: {"Action": "PUT", "Value": FIELD_BIG}}), ) self.assertEqual( {u"ConsumedCapacityUnits": 2}, db.layer1.update_item(TABLE_NAME2, key, {FIELD_NAME: {"Action": "PUT", "Value": FIELD_SMALL}}), ) def test_update_item_delete_primary_key_fails(self): from ddbmock import connect_boto_patch from boto.dynamodb.exceptions import DynamoDBValidationError db = connect_boto_patch() key = {u"HashKeyElement": {TABLE_HK_TYPE: HK_VALUE}, u"RangeKeyElement": {TABLE_RK_TYPE: RK_VALUE}} self.assertRaises( DynamoDBValidationError, db.layer1.update_item, TABLE_NAME, key, {TABLE_RK_NAME: {"Action": "DELETE"}} ) self.assertEqual({TABLE_RK_TYPE: RK_VALUE}, self.t1.store[HK_VALUE, RK_VALUE][TABLE_RK_NAME]) self.assertRaises( DynamoDBValidationError, db.layer1.update_item, TABLE_NAME, key, {TABLE_HK_NAME: {"Action": "DELETE"}} ) self.assertEqual({TABLE_HK_TYPE: HK_VALUE}, self.t1.store[HK_VALUE, RK_VALUE][TABLE_HK_NAME]) def test_update_item_delete_field_ok(self): from ddbmock import connect_boto_patch from boto.dynamodb.exceptions import DynamoDBValidationError db = connect_boto_patch() key = {u"HashKeyElement": {TABLE_HK_TYPE: HK_VALUE}, u"RangeKeyElement": {TABLE_RK_TYPE: RK_VALUE}} db.layer1.update_item(TABLE_NAME, key, {FIELD_NAME: {"Action": "DELETE"}}) self.assertNotIn(FIELD_NAME, self.t1.store[HK_VALUE, RK_VALUE]) # Attempt to delete non-existing field, do nothing db.layer1.update_item(TABLE_NAME, key, {FIELD_NAME_404: {"Action": "DELETE"}}) def test_update_item_delete_field_set_ok(self): from ddbmock import connect_boto_patch from boto.dynamodb.exceptions import DynamoDBValidationError db = connect_boto_patch() expected1 = {u"SS": [u"item1", u"item2", u"item3", u"item4"]} expected2 = {u"SS": [u"item3", u"item1"]} key = {u"HashKeyElement": {TABLE_HK_TYPE: HK_VALUE}, u"RangeKeyElement": {TABLE_RK_TYPE: RK_VALUE}} # Can not remove a scalar value, even from a set self.assertRaises( DynamoDBValidationError, db.layer1.update_item, TABLE_NAME, key, {FIELD_SET_NAME: {"Action": "DELETE", u"Value": {u"S": u"item1"}}}, ) self.assertEqual(expected1, self.t1.store[HK_VALUE, RK_VALUE][FIELD_SET_NAME]) # remove a couple of existing or not item from the field db.layer1.update_item( TABLE_NAME, key, {FIELD_SET_NAME: {"Action": "DELETE", u"Value": {u"SS": [u"item2", u"item4", u"item6"]}}} ) self.assertEqual(expected2, self.t1.store[HK_VALUE, RK_VALUE][FIELD_SET_NAME]) # Field shoud disapear (Empty) db.layer1.update_item( TABLE_NAME, key, {FIELD_SET_NAME: {"Action": "DELETE", u"Value": {u"SS": [u"item1", u"item3", u"item6"]}}} ) self.assertNotIn(FIELD_SET_NAME, self.t1.store[HK_VALUE, RK_VALUE]) def test_update_item_delete_field_set_bad_type(self): from ddbmock import connect_boto_patch from boto.dynamodb.exceptions import DynamoDBValidationError db = connect_boto_patch() expected = {u"SS": [u"item1", u"item2", u"item3", u"item4"]} key = {u"HashKeyElement": {TABLE_HK_TYPE: HK_VALUE}, u"RangeKeyElement": {TABLE_RK_TYPE: RK_VALUE}} self.assertRaises( DynamoDBValidationError, db.layer1.update_item, TABLE_NAME, key, {FIELD_SET_NAME: {"Action": "DELETE", u"Value": {u"B": u"item1"}}}, ) self.assertEqual(expected, self.t1.store[HK_VALUE, RK_VALUE][FIELD_SET_NAME]) self.assertRaises( DynamoDBValidationError, db.layer1.update_item, TABLE_NAME, key, {FIELD_SET_NAME: {"Action": "DELETE", u"Value": {u"BS": [u"item2", u"item4", u"item6"]}}}, ) self.assertEqual(expected, self.t1.store[HK_VALUE, RK_VALUE][FIELD_SET_NAME]) def test_update_item_increment(self): from ddbmock import connect_boto_patch from boto.dynamodb.exceptions import DynamoDBValidationError db = connect_boto_patch() key = {u"HashKeyElement": {TABLE_HK_TYPE: HK_VALUE}} ADD_VALUE = 2 expected = {u"N": unicode(42 + ADD_VALUE)} # regular increment db.layer1.update_item( TABLE_NAME2, key, {FIELD_NUM_NAME: {"Action": "ADD", u"Value": {u"N": unicode(ADD_VALUE)}}} ) self.assertEqual(expected, self.t2.store[HK_VALUE, False][FIELD_NUM_NAME]) def test_update_item_push_to_set_ok(self): from ddbmock import connect_boto_patch from boto.dynamodb.exceptions import DynamoDBValidationError db = connect_boto_patch() expected1 = {u"SS": [u"item1", u"item2", u"item3", u"item4"]} expected2 = {u"SS": [u"item2", u"item3", u"item1", u"item4", u"item5"]} key = {u"HashKeyElement": {TABLE_HK_TYPE: HK_VALUE}, u"RangeKeyElement": {TABLE_RK_TYPE: RK_VALUE}} self.assertRaises( DynamoDBValidationError, db.layer1.update_item, TABLE_NAME, key, {FIELD_SET_NAME: {"Action": "ADD", u"Value": {u"S": u"item5"}}}, ) self.assertEqual(expected1, self.t1.store[HK_VALUE, RK_VALUE][FIELD_SET_NAME]) db.layer1.update_item(TABLE_NAME, key, {FIELD_SET_NAME: {"Action": "ADD", u"Value": {u"SS": [u"item5"]}}}) self.assertEqual(expected2, self.t1.store[HK_VALUE, RK_VALUE][FIELD_SET_NAME]) def test_update_item_push_to_non_set_fail(self): # sometimes weird black magic types occures in test. these are related # to internal "DB" logic. it does not affect real API output at all from ddbmock import connect_boto_patch from boto.dynamodb.exceptions import DynamoDBValidationError db = connect_boto_patch() expected1 = {u"SS": [u"item1", u"item2", u"item3", u"item4"]} key = {u"HashKeyElement": {TABLE_HK_TYPE: HK_VALUE}, u"RangeKeyElement": {TABLE_RK_TYPE: RK_VALUE}} self.assertRaises( DynamoDBValidationError, db.layer1.update_item, TABLE_NAME, key, {FIELD_NAME: {"Action": "ADD", u"Value": {u"B": u"item5"}}}, ) self.assertEqual(expected1, self.t1.store[HK_VALUE, RK_VALUE][FIELD_SET_NAME]) def test_update_return_all_old(self): from ddbmock import connect_boto_patch from boto.dynamodb.exceptions import DynamoDBValidationError key = {u"HashKeyElement": {TABLE_HK_TYPE: HK_VALUE}} ADD_VALUE = 1 db = connect_boto_patch() expected = cp(ITEM2) # regular increment ret = db.layer1.update_item( TABLE_NAME2, key, {FIELD_NUM_NAME: {"Action": "ADD", u"Value": {u"N": unicode(ADD_VALUE)}}}, return_values=u"ALL_OLD", ) self.assertEqual(expected, ret[u"Attributes"]) def test_update_return_all_new(self): from ddbmock import connect_boto_patch from boto.dynamodb.exceptions import DynamoDBValidationError key = {u"HashKeyElement": {TABLE_HK_TYPE: HK_VALUE}} ADD_VALUE = 1 db = connect_boto_patch() expected = cp(ITEM2) expected[FIELD_NUM_NAME][u"N"] = unicode(int(expected[FIELD_NUM_NAME][u"N"]) + ADD_VALUE) # regular increment ret = db.layer1.update_item( TABLE_NAME2, key, {FIELD_NUM_NAME: {"Action": "ADD", u"Value": {u"N": unicode(ADD_VALUE)}}}, return_values=u"ALL_NEW", ) self.assertEqual(expected, ret[u"Attributes"]) def test_update_return_updated_old(self): from ddbmock import connect_boto_patch from boto.dynamodb.exceptions import DynamoDBValidationError key = {u"HashKeyElement": {TABLE_HK_TYPE: HK_VALUE}} ADD_VALUE = 1 db = connect_boto_patch() expected = {FIELD_NUM_NAME: cp(ITEM2[FIELD_NUM_NAME])} # regular increment ret = db.layer1.update_item( TABLE_NAME2, key, {FIELD_NUM_NAME: {"Action": "ADD", u"Value": {u"N": unicode(ADD_VALUE)}}}, return_values=u"UPDATED_OLD", ) self.assertEqual(expected, ret[u"Attributes"]) def test_update_return_updated_new(self): from ddbmock import connect_boto_patch from boto.dynamodb.exceptions import DynamoDBValidationError key = {u"HashKeyElement": {TABLE_HK_TYPE: HK_VALUE}} ADD_VALUE = 1 db = connect_boto_patch() expected = {FIELD_NUM_NAME: cp(ITEM2[FIELD_NUM_NAME])} expected[FIELD_NUM_NAME][u"N"] = unicode(int(expected[FIELD_NUM_NAME][u"N"]) + ADD_VALUE) # regular increment ret = db.layer1.update_item( TABLE_NAME2, key, {FIELD_NUM_NAME: {"Action": "ADD", u"Value": {u"N": unicode(ADD_VALUE)}}}, return_values=u"UPDATED_NEW", ) self.assertEqual(expected, ret[u"Attributes"]) def test_update_item_put_h_oversized(self): from boto.dynamodb.exceptions import DynamoDBValidationError from ddbmock import connect_boto_patch db = connect_boto_patch() key = {u"HashKeyElement": {TABLE_HK_TYPE: HK_VALUE}} # PUT explicite, existing field self.assertRaisesRegexp( DynamoDBValidationError, "Item size.*exceeded", db.layer1.update_item, TABLE_NAME2, key, {"relevant_data": {"Value": RELEVANT_HUGE_FIELD}}, ) self.assertEqual(ITEM[FIELD_NAME], self.t2.store[HK_VALUE, False]["relevant_data"])
def setUp(self): from ddbmock.database.table import Table self.table = Table(NAME, RT, WT, None, None, status="ACTIVE")
class TestQuery(unittest.TestCase): def setUp(self): from ddbmock.database.db import dynamodb from ddbmock.database.table import Table from ddbmock.database.key import PrimaryKey dynamodb.hard_reset() hash_key = PrimaryKey(TABLE_HK_NAME, TABLE_HK_TYPE) range_key = PrimaryKey(TABLE_RK_NAME, TABLE_RK_TYPE) self.t1 = Table(TABLE_NAME, TABLE_RT, TABLE_WT, hash_key, range_key) dynamodb.data[TABLE_NAME] = self.t1 self.t1.put(ITEM1, {}) self.t1.put(ITEM2, {}) self.t1.put(ITEM3, {}) self.t1.put(ITEM4, {}) self.t1.put(ITEM5, {}) def tearDown(self): from ddbmock.database.db import dynamodb from ddbmock import clean_boto_patch dynamodb.hard_reset() clean_boto_patch() def test_query_all(self): from ddbmock import connect_boto_patch from ddbmock.database.db import dynamodb expected = { u"Count": 5, u"Items": [ITEM1, ITEM2, ITEM3, ITEM4, ITEM5], u"ConsumedCapacityUnits": 0.5, } db = connect_boto_patch() ret = db.layer1.query(TABLE_NAME, {TABLE_HK_TYPE: HK_VALUE}) self.assertEqual(expected, ret) # Regression test for #9 def test_query_all_404(self): from ddbmock import connect_boto_patch from ddbmock.database.db import dynamodb expected = { u"Count": 0, u'Items': [], u"ConsumedCapacityUnits": 0.5, } db = connect_boto_patch() ret = db.layer1.query(TABLE_NAME, {TABLE_HK_TYPE: HK_VALUE_404}) self.assertEqual(expected, ret) def test_query_2_first(self): from ddbmock import connect_boto_patch from ddbmock.database.db import dynamodb expected = { u"Count": 2, u"Items": [ITEM1, ITEM2], u"ConsumedCapacityUnits": 0.5, u'LastEvaluatedKey': { u'HashKeyElement': {u'N': u'123'}, u'RangeKeyElement': {u'S': u'Waldo-2'}, }, } db = connect_boto_patch() ret = db.layer1.query(TABLE_NAME, {TABLE_HK_TYPE: HK_VALUE}, limit=2) self.assertEqual(expected, ret) def test_query_paged(self): from ddbmock import connect_boto_patch from ddbmock.database.db import dynamodb from boto.dynamodb.exceptions import DynamoDBValidationError esk = { u'HashKeyElement': {u'N': u'123'}, u'RangeKeyElement': {u'S': u'Waldo-3'}, } bad_esk = { u'HashKeyElement': {u'N': u'123.43'}, u'RangeKeyElement': {u'S': u'Waldo-3'}, } expected1 = { u"Count": 3, u"Items": [ITEM1, ITEM2, ITEM3], u"ConsumedCapacityUnits": 0.5, u'LastEvaluatedKey': esk, } expected2 = { u"Count": 2, u"Items": [ITEM4, ITEM5], u"ConsumedCapacityUnits": 0.5, } db = connect_boto_patch() ret = db.layer1.query(TABLE_NAME, {TABLE_HK_TYPE: HK_VALUE}, limit=3) self.assertEqual(expected1, ret) ret = db.layer1.query(TABLE_NAME, {TABLE_HK_TYPE: HK_VALUE}, limit=3, exclusive_start_key=esk) self.assertEqual(expected2, ret) self.assertRaises(DynamoDBValidationError, db.layer1.query, TABLE_NAME, {TABLE_HK_TYPE: HK_VALUE}, limit=3, exclusive_start_key=bad_esk) def test_query_2_last(self): from ddbmock import connect_boto_patch from ddbmock.database.db import dynamodb expected = { u"Count": 2, u"Items": [ITEM5, ITEM4], u"ConsumedCapacityUnits": 0.5, u'LastEvaluatedKey': { u'HashKeyElement': {u'N': u'123'}, u'RangeKeyElement': {u'S': u'Waldo-4'}, } } db = connect_boto_patch() ret = db.layer1.query(TABLE_NAME, {TABLE_HK_TYPE: HK_VALUE}, limit=2, scan_index_forward=False) self.assertEqual(expected, ret) def test_query_all_filter_fields(self): from ddbmock import connect_boto_patch from ddbmock.database.db import dynamodb expected = { u"Count": 5, u"Items": [ {u"relevant_data": {u"S": "tata"}}, {u"relevant_data": {u"S": "tete"}}, {u"relevant_data": {u"S": "titi"}}, {u"relevant_data": {u"S": "toto"}}, {u"relevant_data": {u"S": "tutu"}}, ], u"ConsumedCapacityUnits": 0.5, } fields = [u'relevant_data'] db = connect_boto_patch() ret = db.layer1.query(TABLE_NAME, {TABLE_HK_TYPE: HK_VALUE}, None, fields) self.assertEqual(expected, ret) # No need to test all conditions/type mismatch as they are unit tested def test_query_condition_filter_fields(self): from ddbmock import connect_boto_patch from ddbmock.database.db import dynamodb expected = { u"Count": 3, u"Items": [ {u"relevant_data": {u"S": u"titi"}}, {u"relevant_data": {u"S": u"toto"}}, {u"relevant_data": {u"S": u"tutu"}}, ], u"ConsumedCapacityUnits": 0.5, } condition = {"AttributeValueList":[{"S":"Waldo-2"}],"ComparisonOperator":"GT"} fields = [u'relevant_data'] db = connect_boto_patch() ret = db.layer1.query(TABLE_NAME, {TABLE_HK_TYPE: HK_VALUE}, condition, fields) self.assertEqual(expected, ret) def test_query_all_consistent(self): from ddbmock import connect_boto_patch from ddbmock.database.db import dynamodb expected = { u"Count": 5, u"Items": [ITEM1, ITEM2, ITEM3, ITEM4, ITEM5], u"ConsumedCapacityUnits": 1, } db = connect_boto_patch() ret = db.layer1.query(TABLE_NAME, {TABLE_HK_TYPE: HK_VALUE}, consistent_read=True) self.assertEqual(expected, ret) def test_query_invalid_condition_multiple_data_in_field(self): from ddbmock import connect_boto_patch from ddbmock.database.db import dynamodb from boto.dynamodb.exceptions import DynamoDBValidationError condition = { "AttributeValueList":[ {"S":"Waldo-2"}, {"S":"Waldo-3"}, ], "ComparisonOperator":"GT" } fields = [u'relevant_data'] db = connect_boto_patch() self.assertRaises(DynamoDBValidationError, db.layer1.query, TABLE_NAME, {TABLE_HK_TYPE: HK_VALUE}, condition, fields)
class TestScan(unittest.TestCase): def setUp(self): from ddbmock.database.db import dynamodb from ddbmock.database.table import Table from ddbmock.database.key import PrimaryKey import helpers self.app = helpers.makeTestApp() dynamodb.hard_reset() hash_key = PrimaryKey(TABLE_HK_NAME, TABLE_HK_TYPE) range_key = PrimaryKey(TABLE_RK_NAME, TABLE_RK_TYPE) self.t1 = Table(TABLE_NAME, TABLE_RT, TABLE_WT, hash_key, range_key) dynamodb.data[TABLE_NAME] = self.t1 self.t1.put(ITEM1, {}) self.t1.put(ITEM2, {}) self.t1.put(ITEM3, {}) self.t1.put(ITEM4, {}) self.t1.put(ITEM5, {}) def tearDown(self): from ddbmock.database.db import dynamodb dynamodb.hard_reset() def test_scan_condition_filter_fields(self): request = { "TableName": TABLE_NAME, "ScanFilter": { "relevant_data": { "AttributeValueList": [ {"S":"toto"}, {"S":"titi"}, {"S":"tata"}, ], "ComparisonOperator": "IN", }, }, "AttributesToGet": [u'relevant_data'], } expected = { u"Count": 3, u"ScannedCount": 5, u"Items": [ {u"relevant_data": {u"S": u"tata"}}, {u"relevant_data": {u"S": u"toto"}}, {u"relevant_data": {u"S": u"titi"}}, ], u"ConsumedCapacityUnits": 0.5, } # Protocol check res = self.app.post_json('/', request, headers=HEADERS, status=200) self.assertEqual(expected, json.loads(res.body)) self.assertEqual('application/x-amz-json-1.0; charset=UTF-8', res.headers['Content-Type']) def test_scan_count_and_attrs_to_get_fails(self): request = { "TableName": TABLE_NAME, "ScanFilter": { "relevant_data": { "AttributeValueList": [ {"S":"toto"}, {"S":"titi"}, {"S":"tata"}, ], "ComparisonOperator": "IN", }, }, "AttributesToGet": [u'relevant_data'], "Count": True, } expected = { u'__type': u'com.amazonaws.dynamodb.v20120810#ValidationException', u'message': u'Can not filter fields when only count is requested' } # Protocol check res = self.app.post_json('/', request, headers=HEADERS, status=400) self.assertEqual(expected, json.loads(res.body)) self.assertEqual('application/x-amz-json-1.0; charset=UTF-8', res.headers['Content-Type'])
class TestBatchWriteItem(unittest.TestCase): def setUp(self): from ddbmock.database.db import dynamodb from ddbmock.database.table import Table from ddbmock.database.key import PrimaryKey dynamodb.hard_reset() hash_key = PrimaryKey(TABLE_HK_NAME, TABLE_HK_TYPE) range_key = PrimaryKey(TABLE_RK_NAME, TABLE_RK_TYPE) self.t1 = Table(TABLE_NAME1, TABLE_RT, TABLE_WT, hash_key, range_key) self.t2 = Table(TABLE_NAME2, TABLE_RT, TABLE_WT, hash_key, None) dynamodb.data[TABLE_NAME1] = self.t1 dynamodb.data[TABLE_NAME2] = self.t2 self.t1.put(ITEM1, {}) self.t2.put(ITEM4, {}) def tearDown(self): from ddbmock.database.db import dynamodb from ddbmock import clean_boto_patch dynamodb.hard_reset() clean_boto_patch() def test_batch_write_item_nominal(self): from ddbmock import connect_boto_patch from ddbmock.database.db import dynamodb db = connect_boto_patch() expected = { "Responses": { TABLE_NAME1: { "ConsumedCapacityUnits": 3 }, TABLE_NAME2: { "ConsumedCapacityUnits": 2 } } } ret = db.layer1.batch_write_item({ TABLE_NAME1: [ { u"DeleteRequest": { u"Key": { u"HashKeyElement": { TABLE_HK_TYPE: HK_VALUE1 }, u"RangeKeyElement": { TABLE_RK_TYPE: RK_VALUE1 }, }, }, }, { u"PutRequest": { u"Item": ITEM2 } }, { u"PutRequest": { u"Item": ITEM3 } }, ], TABLE_NAME2: [ { u"DeleteRequest": { u"Key": { u"HashKeyElement": { TABLE_HK_TYPE: HK_VALUE2 }, }, }, }, { u"PutRequest": { u"Item": ITEM5 } }, ], }) self.assertEqual(expected, ret) def test_batch_write_item_table_404(self): from ddbmock import connect_boto_patch from ddbmock.database.db import dynamodb from boto.exception import BotoServerError db = connect_boto_patch() self.assertRaises( BotoServerError, db.layer1.batch_write_item, { TABLE_NAME_404: [ { u"DeleteRequest": { u"Key": { u"HashKeyElement": { TABLE_HK_TYPE: HK_VALUE1 }, u"RangeKeyElement": { TABLE_RK_TYPE: RK_VALUE1 }, }, }, }, { u"PutRequest": { u"Item": ITEM2 } }, { u"PutRequest": { u"Item": ITEM3 } }, ], TABLE_NAME2: [ { u"DeleteRequest": { u"Key": { u"HashKeyElement": { TABLE_HK_TYPE: HK_VALUE2 }, }, }, }, { u"PutRequest": { u"Item": ITEM5 } }, ], })
class TestUpdateItem(unittest.TestCase): def setUp(self): from ddbmock.database.db import dynamodb from ddbmock.database.table import Table from ddbmock.database.key import PrimaryKey dynamodb.hard_reset() hash_key = PrimaryKey(TABLE_HK_NAME, TABLE_HK_TYPE) range_key = PrimaryKey(TABLE_RK_NAME, TABLE_RK_TYPE) self.t1 = Table(TABLE_NAME, TABLE_RT, TABLE_WT, hash_key, range_key) self.t2 = Table(TABLE_NAME2, TABLE_RT, TABLE_WT, hash_key, None) dynamodb.data[TABLE_NAME] = self.t1 dynamodb.data[TABLE_NAME2] = self.t2 self.t1.put(cp(ITEM), {}) self.t2.put(cp(ITEM2), {}) def tearDown(self): from ddbmock.database.db import dynamodb from ddbmock import clean_boto_patch dynamodb.hard_reset() clean_boto_patch() def test_update_item_put_hr(self): from ddbmock import connect_boto_patch db = connect_boto_patch() key = { u"HashKeyElement": {TABLE_HK_TYPE: HK_VALUE}, u"RangeKeyElement": {TABLE_RK_TYPE: RK_VALUE}, } key2 = { u"HashKeyElement": {TABLE_HK_TYPE: HK_VALUE2}, u"RangeKeyElement": {TABLE_RK_TYPE: RK_VALUE}, } # use PUT as default action, champs existant db.layer1.update_item(TABLE_NAME, key, { 'relevant_data': {'Value': RELEVANT_FIELD} # Move type from 'B' to 'S' }) self.assertEqual(RELEVANT_FIELD, self.t1.store[HK_VALUE, RK_VALUE]['relevant_data']) # PUT explicite, champ non existant db.layer1.update_item(TABLE_NAME, key, { 'irelevant_data': {'Action': 'PUT', 'Value': IRELEVANT_FIELD} }) self.assertEqual(RELEVANT_FIELD, self.t1.store[HK_VALUE, RK_VALUE]['relevant_data']) self.assertEqual(IRELEVANT_FIELD, self.t1.store[HK_VALUE, RK_VALUE]['irelevant_data']) # PUT explicite, item non existant(full item creation) db.layer1.update_item(TABLE_NAME, key2, { 'relevant_data': {'Action': 'PUT', 'Value': RELEVANT_FIELD} }) self.assertEqual({TABLE_HK_TYPE: HK_VALUE2}, self.t1.store[HK_VALUE2, RK_VALUE][TABLE_HK_NAME]) self.assertEqual({TABLE_RK_TYPE: RK_VALUE}, self.t1.store[HK_VALUE2, RK_VALUE][TABLE_RK_NAME]) self.assertEqual(RELEVANT_FIELD, self.t1.store[HK_VALUE2, RK_VALUE]['relevant_data']) def test_update_item_put_h(self): from ddbmock import connect_boto_patch db = connect_boto_patch() key = { u"HashKeyElement": {TABLE_HK_TYPE: HK_VALUE}, } key2 = { u"HashKeyElement": {TABLE_HK_TYPE: HK_VALUE2}, } # use PUT as default action, champs existant db.layer1.update_item(TABLE_NAME2, key, { 'relevant_data': {'Value': RELEVANT_FIELD} # Move type from 'B' to 'S' }) self.assertEqual(RELEVANT_FIELD, self.t2.store[HK_VALUE, False]['relevant_data']) # PUT explicite, champ non existant db.layer1.update_item(TABLE_NAME2, key, { 'irelevant_data': {'Action': 'PUT', 'Value': IRELEVANT_FIELD} }) self.assertEqual(RELEVANT_FIELD, self.t2.store[HK_VALUE, False]['relevant_data']) self.assertEqual(IRELEVANT_FIELD, self.t2.store[HK_VALUE, False]['irelevant_data']) # PUT explicite, item non existant(full item creation) db.layer1.update_item(TABLE_NAME2, key2, { 'relevant_data': {'Action': 'PUT', 'Value': RELEVANT_FIELD} }) self.assertEqual({TABLE_HK_TYPE: HK_VALUE2}, self.t2.store[HK_VALUE2, False][TABLE_HK_NAME]) self.assertEqual(RELEVANT_FIELD, self.t2.store[HK_VALUE2, False]['relevant_data']) def test_put_check_throughput_max_old_new(self): from ddbmock import connect_boto_patch from ddbmock.database.db import dynamodb db = connect_boto_patch() key = {u"HashKeyElement": {TABLE_HK_TYPE: HK_VALUE}} self.assertEqual( {u'ConsumedCapacityUnits': 1}, db.layer1.update_item(TABLE_NAME2, key, {FIELD_NAME: {'Action': 'PUT', 'Value': FIELD_SMALL}}), ) self.assertEqual( {u'ConsumedCapacityUnits': 2}, db.layer1.update_item(TABLE_NAME2, key, {FIELD_NAME: {'Action': 'PUT', 'Value': FIELD_BIG}}), ) self.assertEqual( {u'ConsumedCapacityUnits': 2}, db.layer1.update_item(TABLE_NAME2, key, {FIELD_NAME: {'Action': 'PUT', 'Value': FIELD_SMALL}}), ) def test_update_item_delete_primary_key_fails(self): from ddbmock import connect_boto_patch from boto.dynamodb.exceptions import DynamoDBValidationError db = connect_boto_patch() key = { u"HashKeyElement": {TABLE_HK_TYPE: HK_VALUE}, u"RangeKeyElement": {TABLE_RK_TYPE: RK_VALUE}, } self.assertRaises(DynamoDBValidationError, db.layer1.update_item, TABLE_NAME, key, {TABLE_RK_NAME: {'Action': 'DELETE'}} ) self.assertEqual({TABLE_RK_TYPE: RK_VALUE}, self.t1.store[HK_VALUE, RK_VALUE][TABLE_RK_NAME]) self.assertRaises(DynamoDBValidationError, db.layer1.update_item, TABLE_NAME, key, {TABLE_HK_NAME: {'Action': 'DELETE'}} ) self.assertEqual({TABLE_HK_TYPE: HK_VALUE}, self.t1.store[HK_VALUE, RK_VALUE][TABLE_HK_NAME]) def test_update_item_delete_field_ok(self): from ddbmock import connect_boto_patch from boto.dynamodb.exceptions import DynamoDBValidationError db = connect_boto_patch() key = { u"HashKeyElement": {TABLE_HK_TYPE: HK_VALUE}, u"RangeKeyElement": {TABLE_RK_TYPE: RK_VALUE}, } db.layer1.update_item(TABLE_NAME, key, { FIELD_NAME: {'Action': 'DELETE'}, }) self.assertNotIn(FIELD_NAME, self.t1.store[HK_VALUE, RK_VALUE]) # Attempt to delete non-existing field, do nothing db.layer1.update_item(TABLE_NAME, key, { FIELD_NAME_404: {'Action': 'DELETE'}, }) def test_update_item_delete_field_set_ok(self): from ddbmock import connect_boto_patch from boto.dynamodb.exceptions import DynamoDBValidationError db = connect_boto_patch() expected1 = {u'SS': [u'item1', u'item2', u'item3', u'item4']} expected2 = {u'SS': [u'item3', u'item1']} key = { u"HashKeyElement": {TABLE_HK_TYPE: HK_VALUE}, u"RangeKeyElement": {TABLE_RK_TYPE: RK_VALUE}, } # Can not remove a scalar value, even from a set self.assertRaises(DynamoDBValidationError, db.layer1.update_item, TABLE_NAME, key, { FIELD_SET_NAME: {'Action': 'DELETE', u'Value': {u'S': u'item1'}}, } ) self.assertEqual(expected1, self.t1.store[HK_VALUE, RK_VALUE][FIELD_SET_NAME]) # remove a couple of existing or not item from the field db.layer1.update_item(TABLE_NAME, key, { FIELD_SET_NAME: {'Action': 'DELETE', u'Value': {u'SS': [u'item2', u'item4', u'item6']}}, }) self.assertEqual(expected2, self.t1.store[HK_VALUE, RK_VALUE][FIELD_SET_NAME]) # Field shoud disapear (Empty) db.layer1.update_item(TABLE_NAME, key, { FIELD_SET_NAME: {'Action': 'DELETE', u'Value': {u'SS': [u'item1', u'item3', u'item6']}}, }) self.assertNotIn(FIELD_SET_NAME, self.t1.store[HK_VALUE, RK_VALUE]) def test_update_item_delete_field_set_bad_type(self): from ddbmock import connect_boto_patch from boto.dynamodb.exceptions import DynamoDBValidationError db = connect_boto_patch() expected = {u'SS': [u'item1', u'item2', u'item3', u'item4']} key = { u"HashKeyElement": {TABLE_HK_TYPE: HK_VALUE}, u"RangeKeyElement": {TABLE_RK_TYPE: RK_VALUE}, } self.assertRaises(DynamoDBValidationError, db.layer1.update_item, TABLE_NAME, key, { FIELD_SET_NAME: {'Action': 'DELETE', u'Value': {u'B': u'item1'}}, } ) self.assertEqual(expected, self.t1.store[HK_VALUE, RK_VALUE][FIELD_SET_NAME]) self.assertRaises(DynamoDBValidationError, db.layer1.update_item, TABLE_NAME, key, { FIELD_SET_NAME: {'Action': 'DELETE', u'Value': {u'BS': [u'item2', u'item4', u'item6']}}, } ) self.assertEqual(expected, self.t1.store[HK_VALUE, RK_VALUE][FIELD_SET_NAME]) def test_update_item_increment(self): from ddbmock import connect_boto_patch from boto.dynamodb.exceptions import DynamoDBValidationError db = connect_boto_patch() key = { u"HashKeyElement": {TABLE_HK_TYPE: HK_VALUE}, } ADD_VALUE = 2 expected = {u'N': unicode(42 + ADD_VALUE)} # regular increment db.layer1.update_item(TABLE_NAME2, key, { FIELD_NUM_NAME: {'Action': 'ADD', u'Value': {u'N': unicode(ADD_VALUE)}}, }) self.assertEqual(expected, self.t2.store[HK_VALUE, False][FIELD_NUM_NAME]) def test_update_item_push_to_set_ok(self): from ddbmock import connect_boto_patch from boto.dynamodb.exceptions import DynamoDBValidationError db = connect_boto_patch() expected1 = {u'SS': [u'item1', u'item2', u'item3', u'item4']} expected2 = {u'SS': [u'item2', u'item3', u'item1', u'item4', u'item5']} key = { u"HashKeyElement": {TABLE_HK_TYPE: HK_VALUE}, u"RangeKeyElement": {TABLE_RK_TYPE: RK_VALUE}, } self.assertRaises(DynamoDBValidationError, db.layer1.update_item, TABLE_NAME, key, { FIELD_SET_NAME: {'Action': 'ADD', u'Value': {u'S': u'item5'}}, } ) self.assertEqual(expected1, self.t1.store[HK_VALUE, RK_VALUE][FIELD_SET_NAME]) db.layer1.update_item(TABLE_NAME, key, { FIELD_SET_NAME: {'Action': 'ADD', u'Value': {u'SS': [u'item5']}}, }) self.assertEqual(expected2, self.t1.store[HK_VALUE, RK_VALUE][FIELD_SET_NAME]) def test_update_item_push_to_non_set_fail(self): # sometimes weird black magic types occures in test. these are related # to internal "DB" logic. it does not affect real API output at all from ddbmock import connect_boto_patch from boto.dynamodb.exceptions import DynamoDBValidationError db = connect_boto_patch() expected1 = {u'SS': [u'item1', u'item2', u'item3', u'item4']} key = { u"HashKeyElement": {TABLE_HK_TYPE: HK_VALUE}, u"RangeKeyElement": {TABLE_RK_TYPE: RK_VALUE}, } self.assertRaises(DynamoDBValidationError, db.layer1.update_item, TABLE_NAME, key, { FIELD_NAME: {'Action': 'ADD', u'Value': {u'B': u'item5'}}, } ) self.assertEqual(expected1, self.t1.store[HK_VALUE, RK_VALUE][FIELD_SET_NAME]) def test_update_return_all_old(self): from ddbmock import connect_boto_patch from boto.dynamodb.exceptions import DynamoDBValidationError key = {u"HashKeyElement": {TABLE_HK_TYPE: HK_VALUE}} ADD_VALUE = 1 db = connect_boto_patch() expected = cp(ITEM2) # regular increment ret = db.layer1.update_item(TABLE_NAME2, key, { FIELD_NUM_NAME: {'Action': 'ADD', u'Value': {u'N': unicode(ADD_VALUE)}}, }, return_values=u'ALL_OLD', ) self.assertEqual(expected, ret[u'Attributes']) def test_update_return_all_new(self): from ddbmock import connect_boto_patch from boto.dynamodb.exceptions import DynamoDBValidationError key = {u"HashKeyElement": {TABLE_HK_TYPE: HK_VALUE}} ADD_VALUE = 1 db = connect_boto_patch() expected = cp(ITEM2) expected[FIELD_NUM_NAME][u'N'] = unicode(int(expected[FIELD_NUM_NAME][u'N']) + ADD_VALUE) # regular increment ret = db.layer1.update_item(TABLE_NAME2, key, { FIELD_NUM_NAME: {'Action': 'ADD', u'Value': {u'N': unicode(ADD_VALUE)}}, }, return_values=u'ALL_NEW', ) self.assertEqual(expected, ret[u'Attributes']) def test_update_return_updated_old(self): from ddbmock import connect_boto_patch from boto.dynamodb.exceptions import DynamoDBValidationError key = {u"HashKeyElement": {TABLE_HK_TYPE: HK_VALUE}} ADD_VALUE = 1 db = connect_boto_patch() expected = {FIELD_NUM_NAME: cp(ITEM2[FIELD_NUM_NAME])} # regular increment ret = db.layer1.update_item(TABLE_NAME2, key, { FIELD_NUM_NAME: {'Action': 'ADD', u'Value': {u'N': unicode(ADD_VALUE)}}, }, return_values=u'UPDATED_OLD', ) self.assertEqual(expected, ret[u'Attributes']) def test_update_return_updated_new(self): from ddbmock import connect_boto_patch from boto.dynamodb.exceptions import DynamoDBValidationError key = {u"HashKeyElement": {TABLE_HK_TYPE: HK_VALUE}} ADD_VALUE = 1 db = connect_boto_patch() expected = {FIELD_NUM_NAME: cp(ITEM2[FIELD_NUM_NAME])} expected[FIELD_NUM_NAME][u'N'] = unicode(int(expected[FIELD_NUM_NAME][u'N']) + ADD_VALUE) # regular increment ret = db.layer1.update_item(TABLE_NAME2, key, { FIELD_NUM_NAME: {'Action': 'ADD', u'Value': {u'N': unicode(ADD_VALUE)}}, }, return_values=u'UPDATED_NEW', ) self.assertEqual(expected, ret[u'Attributes']) def test_update_item_put_h_oversized(self): from boto.dynamodb.exceptions import DynamoDBValidationError from ddbmock import connect_boto_patch db = connect_boto_patch() key = { u"HashKeyElement": {TABLE_HK_TYPE: HK_VALUE}, } # PUT explicite, existing field self.assertRaisesRegexp(DynamoDBValidationError, 'Item size.*exceeded', db.layer1.update_item, TABLE_NAME2, key, { 'relevant_data': {'Value': RELEVANT_HUGE_FIELD}, }) self.assertEqual(ITEM[FIELD_NAME], self.t2.store[HK_VALUE, False]['relevant_data'])
class TestBatchGetItem(unittest.TestCase): def setUp(self): from ddbmock.database.db import dynamodb from ddbmock.database.table import Table from ddbmock.database.key import PrimaryKey dynamodb.hard_reset() hash_key = PrimaryKey(TABLE_HK_NAME, TABLE_HK_TYPE) range_key = PrimaryKey(TABLE_RK_NAME, TABLE_RK_TYPE) self.t1 = Table(TABLE_NAME1, TABLE_RT, TABLE_WT, hash_key, range_key) self.t2 = Table(TABLE_NAME2, TABLE_RT, TABLE_WT, hash_key, None) dynamodb.data[TABLE_NAME1] = self.t1 dynamodb.data[TABLE_NAME2] = self.t2 self.t1.put(ITEM1, {}) self.t1.put(ITEM2, {}) self.t1.put(ITEM3, {}) self.t2.put(ITEM4, {}) self.t2.put(ITEM5, {}) self.t2.put(ITEM_BIG, {}) def tearDown(self): from ddbmock.database.db import dynamodb from ddbmock import clean_boto_patch dynamodb.hard_reset() clean_boto_patch() def test_batch_get_item_nominal(self): from ddbmock import connect_boto_patch from ddbmock.database.db import dynamodb db = connect_boto_patch() expected = { "Responses": { "Table-HR": {"Items": [ITEM1, ITEM2], "ConsumedCapacityUnits": 1.0}, "Table-H": {"Items": [ITEM5, ITEM_BIG], "ConsumedCapacityUnits": 1.5}, } } ret = db.layer1.batch_get_item( { TABLE_NAME1: { u"Keys": [ {u"HashKeyElement": {TABLE_HK_TYPE: HK_VALUE1}, u"RangeKeyElement": {TABLE_RK_TYPE: RK_VALUE1}}, {u"HashKeyElement": {TABLE_HK_TYPE: HK_VALUE1}, u"RangeKeyElement": {TABLE_RK_TYPE: RK_VALUE2}}, ] }, TABLE_NAME2: { u"Keys": [ {u"HashKeyElement": {TABLE_HK_TYPE: HK_VALUE3}}, {u"HashKeyElement": {TABLE_HK_TYPE: HK_VALUE1}}, {u"HashKeyElement": {TABLE_HK_TYPE: u"404"}}, ] }, } ) self.assertEqual(expected, ret) def test_batch_get_item_filter_one(self): from ddbmock import connect_boto_patch from ddbmock.database.db import dynamodb db = connect_boto_patch() expected = { "Responses": { "Table-HR": { "Items": [{"relevant_data": {"S": "tata"}}, {"relevant_data": {"S": "tata"}}], "ConsumedCapacityUnits": 1.0, }, "Table-H": { "Items": [ {"relevant_data": {"S": "tutu"}, "hash_key": {"N": "789"}, "range_key": {"S": "Waldo-5"}} ], "ConsumedCapacityUnits": 0.5, }, } } ret = db.layer1.batch_get_item( { TABLE_NAME1: { u"Keys": [ {u"HashKeyElement": {TABLE_HK_TYPE: HK_VALUE1}, u"RangeKeyElement": {TABLE_RK_TYPE: RK_VALUE1}}, {u"HashKeyElement": {TABLE_HK_TYPE: HK_VALUE1}, u"RangeKeyElement": {TABLE_RK_TYPE: RK_VALUE1}}, ], u"AttributesToGet": [u"relevant_data"], }, TABLE_NAME2: {u"Keys": [{u"HashKeyElement": {TABLE_HK_TYPE: HK_VALUE3}}]}, } ) self.assertEqual(expected, ret) def test_batch_get_item_table_404(self): from ddbmock import connect_boto_patch from ddbmock.database.db import dynamodb from boto.exception import BotoServerError db = connect_boto_patch() self.assertRaises( BotoServerError, db.layer1.batch_get_item, { TABLE_NAME_404: { u"Keys": [ {u"HashKeyElement": {TABLE_HK_TYPE: HK_VALUE1}, u"RangeKeyElement": {TABLE_RK_TYPE: RK_VALUE1}}, {u"HashKeyElement": {TABLE_HK_TYPE: HK_VALUE1}, u"RangeKeyElement": {TABLE_RK_TYPE: RK_VALUE1}}, ] }, TABLE_NAME2: {u"Keys": [{u"HashKeyElement": {TABLE_HK_TYPE: HK_VALUE3}}]}, }, ) def test_batch_get_item_bad_key(self): from ddbmock import connect_boto_patch from ddbmock.database.db import dynamodb from boto.dynamodb.exceptions import DynamoDBValidationError db = connect_boto_patch() self.assertRaises( DynamoDBValidationError, db.layer1.batch_get_item, { TABLE_NAME1: { u"Keys": [ {u"HashKeyElement": {TABLE_HK_TYPE: HK_VALUE1}, u"RangeKeyElement": {TABLE_RK_TYPE: RK_VALUE1}}, {u"HashKeyElement": {TABLE_HK_TYPE: HK_VALUE1}}, ] }, TABLE_NAME2: {u"Keys": [{u"HashKeyElement": {TABLE_HK_TYPE: HK_VALUE3}}]}, }, ) self.assertRaises( DynamoDBValidationError, db.layer1.batch_get_item, { TABLE_NAME1: { u"Keys": [ {u"HashKeyElement": {TABLE_HK_TYPE: HK_VALUE1}, u"RangeKeyElement": {TABLE_RK_TYPE: RK_VALUE1}}, {u"HashKeyElement": {TABLE_HK_TYPE: HK_VALUE1}, u"RangeKeyElement": {TABLE_RK_TYPE: RK_VALUE1}}, ] }, TABLE_NAME2: { u"Keys": [ {u"HashKeyElement": {TABLE_HK_TYPE: HK_VALUE3}, u"RangeKeyElement": {TABLE_RK_TYPE: RK_VALUE1}} ] }, }, )
class TestGetItem(unittest.TestCase): def setUp(self): from ddbmock.database.db import dynamodb from ddbmock.database.table import Table from ddbmock.database.key import PrimaryKey dynamodb.hard_reset() hash_key = PrimaryKey(TABLE_HK_NAME, TABLE_HK_TYPE) range_key = PrimaryKey(TABLE_RK_NAME, TABLE_RK_TYPE) self.t1 = Table(TABLE_NAME, TABLE_RT, TABLE_WT, hash_key, range_key) self.t2 = Table(TABLE_NAME2, TABLE_RT, TABLE_WT, hash_key, None) dynamodb.data[TABLE_NAME] = self.t1 dynamodb.data[TABLE_NAME2] = self.t2 self.t1.put(ITEM, {}) self.t2.put(ITEM2, {}) self.t2.put(ITEM_BIG, {}) def tearDown(self): from ddbmock.database.db import dynamodb from ddbmock import clean_boto_patch dynamodb.hard_reset() clean_boto_patch() def test_get_hr(self): from ddbmock import connect_boto_patch from ddbmock.database.db import dynamodb db = connect_boto_patch() expected = { u'ConsumedCapacityUnits': 0.5, u'Item': ITEM, } key = { u"HashKeyElement": { TABLE_HK_TYPE: HK_VALUE }, u"RangeKeyElement": { TABLE_RK_TYPE: RK_VALUE }, } self.assertEquals(expected, db.layer1.get_item(TABLE_NAME, key)) def test_get_hr_consistent(self): from ddbmock import connect_boto_patch from ddbmock.database.db import dynamodb db = connect_boto_patch() expected = { u'ConsumedCapacityUnits': 1, u'Item': ITEM, } key = { u"HashKeyElement": { TABLE_HK_TYPE: HK_VALUE }, u"RangeKeyElement": { TABLE_RK_TYPE: RK_VALUE }, } self.assertEquals( expected, db.layer1.get_item(TABLE_NAME, key, consistent_read=True)) def test_get_h(self): from ddbmock import connect_boto_patch from ddbmock.database.db import dynamodb db = connect_boto_patch() expected = { u'ConsumedCapacityUnits': 0.5, u'Item': ITEM2, } key = { u"HashKeyElement": { TABLE_HK_TYPE: HK_VALUE }, } self.assertEquals(expected, db.layer1.get_item(TABLE_NAME2, key)) def test_get_consistent_big_attributes_to_get(self): from ddbmock import connect_boto_patch from ddbmock.database.db import dynamodb db = connect_boto_patch() expected = { u'ConsumedCapacityUnits': 2, u'Item': { TABLE_HK_NAME: { TABLE_HK_TYPE: HK_VALUE2 } }, } key = { u"HashKeyElement": { TABLE_HK_TYPE: HK_VALUE2 }, } self.assertEquals( expected, db.layer1.get_item(TABLE_NAME2, key, consistent_read=True, attributes_to_get=[TABLE_HK_NAME])) def test_get_h_404(self): from ddbmock import connect_boto_patch from ddbmock.database.db import dynamodb from boto.dynamodb.exceptions import DynamoDBKeyNotFoundError db = connect_boto_patch() key = { u"HashKeyElement": { TABLE_HK_TYPE: HK_VALUE_404 }, } self.assertRaises(DynamoDBKeyNotFoundError, db.layer1.get_item, TABLE_NAME2, key) def test_get_hr_attr_to_get(self): from ddbmock import connect_boto_patch from ddbmock.database.db import dynamodb db = connect_boto_patch() expected = { u'ConsumedCapacityUnits': 0.5, u'Item': { u'relevant_data': { u'B': u'THVkaWEgaXMgdGhlIGJlc3QgY29tcGFueSBldmVyIQ==' } } } key = { u"HashKeyElement": { TABLE_HK_TYPE: HK_VALUE }, u"RangeKeyElement": { TABLE_RK_TYPE: RK_VALUE }, } self.assertEquals( expected, db.layer1.get_item(TABLE_NAME, key, attributes_to_get=[u'relevant_data']))
class TestQuery(unittest.TestCase): def setUp(self): from ddbmock.database.db import dynamodb from ddbmock.database.table import Table from ddbmock.database.key import PrimaryKey dynamodb.hard_reset() hash_key = PrimaryKey(TABLE_HK_NAME, TABLE_HK_TYPE) range_key = PrimaryKey(TABLE_RK_NAME, TABLE_RK_TYPE) self.t1 = Table(TABLE_NAME, TABLE_RT, TABLE_WT, hash_key, range_key) dynamodb.data[TABLE_NAME] = self.t1 self.t1.put(ITEM1, {}) self.t1.put(ITEM2, {}) self.t1.put(ITEM3, {}) self.t1.put(ITEM4, {}) self.t1.put(ITEM5, {}) def tearDown(self): from ddbmock.database.db import dynamodb from ddbmock import clean_boto_patch dynamodb.hard_reset() clean_boto_patch() def test_query_all(self): from ddbmock import connect_boto_patch from ddbmock.database.db import dynamodb expected = { u"Count": 5, u"Items": [ITEM1, ITEM2, ITEM3, ITEM4, ITEM5], u"ConsumedCapacityUnits": 0.5, } db = connect_boto_patch() ret = db.layer1.query(TABLE_NAME, {TABLE_HK_TYPE: HK_VALUE}) self.assertEqual(expected, ret) # Regression test for #9 def test_query_all_404(self): from ddbmock import connect_boto_patch from ddbmock.database.db import dynamodb expected = { u"Count": 0, u'Items': [], u"ConsumedCapacityUnits": 0.5, } db = connect_boto_patch() ret = db.layer1.query(TABLE_NAME, {TABLE_HK_TYPE: HK_VALUE_404}) self.assertEqual(expected, ret) def test_query_2_first(self): from ddbmock import connect_boto_patch from ddbmock.database.db import dynamodb expected = { u"Count": 2, u"Items": [ITEM1, ITEM2], u"ConsumedCapacityUnits": 0.5, u'LastEvaluatedKey': { u'HashKeyElement': { u'N': u'123' }, u'RangeKeyElement': { u'S': u'Waldo-2' }, }, } db = connect_boto_patch() ret = db.layer1.query(TABLE_NAME, {TABLE_HK_TYPE: HK_VALUE}, limit=2) self.assertEqual(expected, ret) def test_query_paged(self): from ddbmock import connect_boto_patch from ddbmock.database.db import dynamodb from boto.dynamodb.exceptions import DynamoDBValidationError esk = { u'HashKeyElement': { u'N': u'123' }, u'RangeKeyElement': { u'S': u'Waldo-3' }, } bad_esk = { u'HashKeyElement': { u'N': u'123.43' }, u'RangeKeyElement': { u'S': u'Waldo-3' }, } expected1 = { u"Count": 3, u"Items": [ITEM1, ITEM2, ITEM3], u"ConsumedCapacityUnits": 0.5, u'LastEvaluatedKey': esk, } expected2 = { u"Count": 2, u"Items": [ITEM4, ITEM5], u"ConsumedCapacityUnits": 0.5, } db = connect_boto_patch() ret = db.layer1.query(TABLE_NAME, {TABLE_HK_TYPE: HK_VALUE}, limit=3) self.assertEqual(expected1, ret) ret = db.layer1.query(TABLE_NAME, {TABLE_HK_TYPE: HK_VALUE}, limit=3, exclusive_start_key=esk) self.assertEqual(expected2, ret) self.assertRaises(DynamoDBValidationError, db.layer1.query, TABLE_NAME, {TABLE_HK_TYPE: HK_VALUE}, limit=3, exclusive_start_key=bad_esk) def test_query_2_last(self): from ddbmock import connect_boto_patch from ddbmock.database.db import dynamodb expected = { u"Count": 2, u"Items": [ITEM5, ITEM4], u"ConsumedCapacityUnits": 0.5, u'LastEvaluatedKey': { u'HashKeyElement': { u'N': u'123' }, u'RangeKeyElement': { u'S': u'Waldo-4' }, } } db = connect_boto_patch() ret = db.layer1.query(TABLE_NAME, {TABLE_HK_TYPE: HK_VALUE}, limit=2, scan_index_forward=False) self.assertEqual(expected, ret) def test_query_all_filter_fields(self): from ddbmock import connect_boto_patch from ddbmock.database.db import dynamodb expected = { u"Count": 5, u"Items": [ { u"relevant_data": { u"S": "tata" } }, { u"relevant_data": { u"S": "tete" } }, { u"relevant_data": { u"S": "titi" } }, { u"relevant_data": { u"S": "toto" } }, { u"relevant_data": { u"S": "tutu" } }, ], u"ConsumedCapacityUnits": 0.5, } fields = [u'relevant_data'] db = connect_boto_patch() ret = db.layer1.query(TABLE_NAME, {TABLE_HK_TYPE: HK_VALUE}, None, fields) self.assertEqual(expected, ret) # No need to test all conditions/type mismatch as they are unit tested def test_query_condition_filter_fields(self): from ddbmock import connect_boto_patch from ddbmock.database.db import dynamodb expected = { u"Count": 3, u"Items": [ { u"relevant_data": { u"S": u"titi" } }, { u"relevant_data": { u"S": u"toto" } }, { u"relevant_data": { u"S": u"tutu" } }, ], u"ConsumedCapacityUnits": 0.5, } condition = { "AttributeValueList": [{ "S": "Waldo-2" }], "ComparisonOperator": "GT" } fields = [u'relevant_data'] db = connect_boto_patch() ret = db.layer1.query(TABLE_NAME, {TABLE_HK_TYPE: HK_VALUE}, condition, fields) self.assertEqual(expected, ret) def test_query_all_consistent(self): from ddbmock import connect_boto_patch from ddbmock.database.db import dynamodb expected = { u"Count": 5, u"Items": [ITEM1, ITEM2, ITEM3, ITEM4, ITEM5], u"ConsumedCapacityUnits": 1, } db = connect_boto_patch() ret = db.layer1.query(TABLE_NAME, {TABLE_HK_TYPE: HK_VALUE}, consistent_read=True) self.assertEqual(expected, ret) def test_query_invalid_condition_multiple_data_in_field(self): from ddbmock import connect_boto_patch from ddbmock.database.db import dynamodb from boto.dynamodb.exceptions import DynamoDBValidationError condition = { "AttributeValueList": [ { "S": "Waldo-2" }, { "S": "Waldo-3" }, ], "ComparisonOperator": "GT" } fields = [u'relevant_data'] db = connect_boto_patch() self.assertRaises(DynamoDBValidationError, db.layer1.query, TABLE_NAME, {TABLE_HK_TYPE: HK_VALUE}, condition, fields)
class TestBatchGetItem(unittest.TestCase): def setUp(self): from ddbmock.database.db import dynamodb from ddbmock.database.table import Table from ddbmock.database.key import PrimaryKey from ddbmock import main app = main({}) from webtest import TestApp self.app = TestApp(app) dynamodb.hard_reset() hash_key = PrimaryKey(TABLE_HK_NAME, TABLE_HK_TYPE) range_key = PrimaryKey(TABLE_RK_NAME, TABLE_RK_TYPE) self.t1 = Table(TABLE_NAME1, TABLE_RT, TABLE_WT, hash_key, range_key) self.t2 = Table(TABLE_NAME2, TABLE_RT, TABLE_WT, hash_key, None) dynamodb.data[TABLE_NAME1] = self.t1 dynamodb.data[TABLE_NAME2] = self.t2 self.t1.put(ITEM1, {}) self.t1.put(ITEM2, {}) self.t1.put(ITEM3, {}) self.t2.put(ITEM4, {}) self.t2.put(ITEM5, {}) def tearDown(self): from ddbmock.database.db import dynamodb dynamodb.hard_reset() def test_batch_get_item_filter_one(self): request = { u"RequestItems": { TABLE_NAME1: { u"Keys": [ { u"HashKeyElement": { TABLE_HK_TYPE: HK_VALUE1 }, u"RangeKeyElement": { TABLE_RK_TYPE: RK_VALUE1 }, }, { u"HashKeyElement": { TABLE_HK_TYPE: HK_VALUE1 }, u"RangeKeyElement": { TABLE_RK_TYPE: RK_VALUE1 } }, ], u"AttributesToGet": [u"relevant_data"], }, TABLE_NAME2: { u"Keys": [ { u"HashKeyElement": { TABLE_HK_TYPE: HK_VALUE3 } }, ], }, } } expected = { "Responses": { "Table-HR": { "Items": [ { "relevant_data": { "S": "tata" } }, { "relevant_data": { "S": "tata" } }, ], "ConsumedCapacityUnits": 1.0 }, "Table-H": { "Items": [ { "relevant_data": { "S": "tutu" }, "hash_key": { "N": "789" }, "range_key": { "S": "Waldo-5" } }, ], "ConsumedCapacityUnits": 0.5 } } } # Protocol check res = self.app.post_json('/', request, headers=HEADERS, status=200) self.assertEqual(expected, json.loads(res.body)) self.assertEqual('application/x-amz-json-1.0; charset=UTF-8', res.headers['Content-Type']) def test_batch_get_item_filter_one_consistent(self): request = { u"RequestItems": { TABLE_NAME1: { u"Keys": [ { u"HashKeyElement": { TABLE_HK_TYPE: HK_VALUE1 }, u"RangeKeyElement": { TABLE_RK_TYPE: RK_VALUE1 } }, { u"HashKeyElement": { TABLE_HK_TYPE: HK_VALUE1 }, u"RangeKeyElement": { TABLE_RK_TYPE: RK_VALUE1 } }, ], u"AttributesToGet": [u"relevant_data"], u"ConsistentRead": True, }, TABLE_NAME2: { u"Keys": [ { u"HashKeyElement": { TABLE_HK_TYPE: HK_VALUE3 } }, ], }, } } expected = { "Responses": { "Table-HR": { "Items": [ { "relevant_data": { "S": "tata" } }, { "relevant_data": { "S": "tata" } }, ], "ConsumedCapacityUnits": 2.0 }, "Table-H": { "Items": [ { "relevant_data": { "S": "tutu" }, "hash_key": { "N": "789" }, "range_key": { "S": "Waldo-5" } }, ], "ConsumedCapacityUnits": 0.5 } } } # Protocol check res = self.app.post_json('/', request, headers=HEADERS, status=200) self.assertEqual(expected, json.loads(res.body)) self.assertEqual('application/x-amz-json-1.0; charset=UTF-8', res.headers['Content-Type'])
class TestGetItem(unittest.TestCase): def setUp(self): from ddbmock.database.db import dynamodb from ddbmock.database.table import Table from ddbmock.database.key import PrimaryKey dynamodb.hard_reset() hash_key = PrimaryKey(TABLE_HK_NAME, TABLE_HK_TYPE) range_key = PrimaryKey(TABLE_RK_NAME, TABLE_RK_TYPE) self.t1 = Table(TABLE_NAME, TABLE_RT, TABLE_WT, hash_key, range_key) self.t2 = Table(TABLE_NAME2, TABLE_RT, TABLE_WT, hash_key, None) dynamodb.data[TABLE_NAME] = self.t1 dynamodb.data[TABLE_NAME2] = self.t2 self.t1.put(ITEM, {}) self.t2.put(ITEM2, {}) self.t2.put(ITEM_BIG, {}) def tearDown(self): from ddbmock.database.db import dynamodb from ddbmock import clean_boto_patch dynamodb.hard_reset() clean_boto_patch() def test_get_hr(self): from ddbmock import connect_boto_patch from ddbmock.database.db import dynamodb db = connect_boto_patch() expected = { u'ConsumedCapacityUnits': 0.5, u'Item': ITEM, } key = { u"HashKeyElement": {TABLE_HK_TYPE: HK_VALUE}, u"RangeKeyElement": {TABLE_RK_TYPE: RK_VALUE}, } self.assertEquals(expected, db.layer1.get_item(TABLE_NAME, key)) def test_get_hr_consistent(self): from ddbmock import connect_boto_patch from ddbmock.database.db import dynamodb db = connect_boto_patch() expected = { u'ConsumedCapacityUnits': 1, u'Item': ITEM, } key = { u"HashKeyElement": {TABLE_HK_TYPE: HK_VALUE}, u"RangeKeyElement": {TABLE_RK_TYPE: RK_VALUE}, } self.assertEquals(expected, db.layer1.get_item(TABLE_NAME, key, consistent_read=True)) def test_get_h(self): from ddbmock import connect_boto_patch from ddbmock.database.db import dynamodb db = connect_boto_patch() expected = { u'ConsumedCapacityUnits': 0.5, u'Item': ITEM2, } key = { u"HashKeyElement": {TABLE_HK_TYPE: HK_VALUE}, } self.assertEquals(expected, db.layer1.get_item(TABLE_NAME2, key)) def test_get_consistent_big_attributes_to_get(self): from ddbmock import connect_boto_patch from ddbmock.database.db import dynamodb db = connect_boto_patch() expected = { u'ConsumedCapacityUnits': 2, u'Item': {TABLE_HK_NAME: {TABLE_HK_TYPE: HK_VALUE2}}, } key = { u"HashKeyElement": {TABLE_HK_TYPE: HK_VALUE2}, } self.assertEquals(expected, db.layer1.get_item(TABLE_NAME2, key, consistent_read=True, attributes_to_get=[TABLE_HK_NAME])) def test_get_h_404(self): from ddbmock import connect_boto_patch from ddbmock.database.db import dynamodb from boto.dynamodb.exceptions import DynamoDBKeyNotFoundError db = connect_boto_patch() key = { u"HashKeyElement": {TABLE_HK_TYPE: HK_VALUE_404}, } self.assertRaises(DynamoDBKeyNotFoundError, db.layer1.get_item, TABLE_NAME2, key) def test_get_hr_attr_to_get(self): from ddbmock import connect_boto_patch from ddbmock.database.db import dynamodb db = connect_boto_patch() expected = { u'ConsumedCapacityUnits': 0.5, u'Item': { u'relevant_data': {u'B': u'THVkaWEgaXMgdGhlIGJlc3QgY29tcGFueSBldmVyIQ=='}} } key = { u"HashKeyElement": {TABLE_HK_TYPE: HK_VALUE}, u"RangeKeyElement": {TABLE_RK_TYPE: RK_VALUE}, } self.assertEquals(expected, db.layer1.get_item(TABLE_NAME, key, attributes_to_get=[u'relevant_data']))
class TestBatchWriteItem(unittest.TestCase): def setUp(self): from ddbmock.database.db import dynamodb from ddbmock.database.table import Table from ddbmock.database.key import PrimaryKey from ddbmock import main app = main({}) from webtest import TestApp self.app = TestApp(app) dynamodb.hard_reset() hash_key = PrimaryKey(TABLE_HK_NAME, TABLE_HK_TYPE) range_key = PrimaryKey(TABLE_RK_NAME, TABLE_RK_TYPE) self.t1 = Table(TABLE_NAME1, TABLE_RT, TABLE_WT, hash_key, range_key) self.t2 = Table(TABLE_NAME2, TABLE_RT, TABLE_WT, hash_key, None) dynamodb.data[TABLE_NAME1] = self.t1 dynamodb.data[TABLE_NAME2] = self.t2 self.t1.put(ITEM1, {}) self.t2.put(ITEM4, {}) def tearDown(self): from ddbmock.database.db import dynamodb dynamodb.hard_reset() def test_batch_write_item(self): request = { u"RequestItems": { TABLE_NAME1: [ { u"DeleteRequest": { u"Key": { u"HashKeyElement": { TABLE_HK_TYPE: HK_VALUE1 }, u"RangeKeyElement": { TABLE_RK_TYPE: RK_VALUE1 }, }, }, }, { u"PutRequest": { u"Item": ITEM2 } }, { u"PutRequest": { u"Item": ITEM3 } }, ], TABLE_NAME2: [ { u"DeleteRequest": { u"Key": { u"HashKeyElement": { TABLE_HK_TYPE: HK_VALUE2 }, }, }, }, { u"PutRequest": { u"Item": ITEM5 } }, ], } } expected = { "Responses": { TABLE_NAME1: { "ConsumedCapacityUnits": 3 }, TABLE_NAME2: { "ConsumedCapacityUnits": 2 } } } # Protocol check res = self.app.post_json('/', request, headers=HEADERS, status=200) self.assertEqual(expected, json.loads(res.body)) self.assertEqual('application/x-amz-json-1.0; charset=UTF-8', res.headers['Content-Type'])
class TestQuery(unittest.TestCase): def setUp(self): from ddbmock.database.db import dynamodb from ddbmock.database.table import Table from ddbmock.database.key import PrimaryKey from ddbmock import main app = main({}) from webtest import TestApp self.app = TestApp(app) dynamodb.hard_reset() hash_key = PrimaryKey(TABLE_HK_NAME, TABLE_HK_TYPE) range_key = PrimaryKey(TABLE_RK_NAME, TABLE_RK_TYPE) self.t1 = Table(TABLE_NAME, TABLE_RT, TABLE_WT, hash_key, range_key) dynamodb.data[TABLE_NAME] = self.t1 self.t1.put(ITEM1, {}) self.t1.put(ITEM2, {}) self.t1.put(ITEM3, {}) self.t1.put(ITEM4, {}) self.t1.put(ITEM5, {}) def tearDown(self): from ddbmock.database.db import dynamodb dynamodb.hard_reset() def test_query_condition_filter_fields(self): request = { "TableName": TABLE_NAME, "HashKeyValue": { TABLE_HK_TYPE: HK_VALUE }, "RangeKeyCondition": { "AttributeValueList": [{ "S": "Waldo-2" }], "ComparisonOperator": "GT", }, "AttributesToGet": [u'relevant_data'], } expected = { u"Count": 3, u"Items": [ { u"relevant_data": { u"S": u"titi" } }, { u"relevant_data": { u"S": u"toto" } }, { u"relevant_data": { u"S": u"tutu" } }, ], u"ConsumedCapacityUnits": 0.5, } # Protocol check res = self.app.post_json('/', request, headers=HEADERS, status=200) self.assertEqual(expected, json.loads(res.body)) self.assertEqual('application/x-amz-json-1.0; charset=UTF-8', res.headers['Content-Type']) def test_query_count_and_attrs_to_get_fails(self): request = { "TableName": TABLE_NAME, "HashKeyValue": { TABLE_HK_TYPE: HK_VALUE }, "RangeKeyCondition": { "AttributeValueList": [{ "S": "Waldo-2" }], "ComparisonOperator": "GT", }, "AttributesToGet": [u'relevant_data'], "Count": True, } expected = { u'__type': u'com.amazonaws.dynamodb.v20111205#ValidationException', u'message': u'Can filter fields when only count is requested' } # Protocol check res = self.app.post_json('/', request, headers=HEADERS, status=400) self.assertEqual(expected, json.loads(res.body)) self.assertEqual('application/x-amz-json-1.0; charset=UTF-8', res.headers['Content-Type']) # Regression test for #9 def test_query_all_404(self): request = { "TableName": TABLE_NAME, "HashKeyValue": { TABLE_HK_TYPE: HK_VALUE_404 }, } expected = { u"Count": 0, u'Items': [], u"ConsumedCapacityUnits": 0.5, } # Protocol check res = self.app.post_json('/', request, headers=HEADERS, status=200) self.assertEqual(expected, json.loads(res.body)) self.assertEqual('application/x-amz-json-1.0; charset=UTF-8', res.headers['Content-Type'])
class TestScan(unittest.TestCase): def setUp(self): from ddbmock.database.db import dynamodb from ddbmock.database.table import Table from ddbmock.database.key import PrimaryKey dynamodb.hard_reset() hash_key = PrimaryKey(TABLE_HK_NAME, TABLE_HK_TYPE) range_key = PrimaryKey(TABLE_RK_NAME, TABLE_RK_TYPE) self.t1 = Table(TABLE_NAME, TABLE_RT, TABLE_WT, hash_key, range_key) dynamodb.data[TABLE_NAME] = self.t1 self.t1.put(ITEM1, {}) self.t1.put(ITEM2, {}) self.t1.put(ITEM3, {}) self.t1.put(ITEM4, {}) self.t1.put(ITEM5, {}) self.t1.put(ITEM6, {}) def tearDown(self): from ddbmock.database.db import dynamodb from ddbmock import clean_boto_patch dynamodb.hard_reset() clean_boto_patch() def test_scan_all(self): from ddbmock import connect_boto_patch from ddbmock.database.db import dynamodb expected = { u"Count": 6, u"ScannedCount": 6, u"Items": [ITEM2, ITEM1, ITEM6, ITEM5, ITEM4, ITEM3], u"ConsumedCapacityUnits": 1.5, } db = connect_boto_patch() ret = db.layer1.scan(TABLE_NAME, None) self.assertEqual(expected, ret) def test_scan_paged(self): from ddbmock import connect_boto_patch from ddbmock.database.db import dynamodb from boto.dynamodb.exceptions import DynamoDBValidationError esk = { u'HashKeyElement': {u'N': u'789'}, u'RangeKeyElement': {u'S': u'Waldo-5'}, } expected1 = { u"Count": 4, u"ScannedCount": 4, u"Items": [ITEM2, ITEM1, ITEM6, ITEM5], u"ConsumedCapacityUnits": 1.5, u'LastEvaluatedKey': esk, } expected2 = { u"Count": 2, u"ScannedCount": 2, u"Items": [ITEM4, ITEM3], u"ConsumedCapacityUnits": 0.5, } db = connect_boto_patch() ret = db.layer1.scan(TABLE_NAME, limit=4) self.assertEqual(expected1, ret) ret = db.layer1.scan(TABLE_NAME, exclusive_start_key=esk) self.assertEqual(expected2, ret) def test_scan_all_filter_fields(self): from ddbmock import connect_boto_patch from ddbmock.database.db import dynamodb self.maxDiff = None expected = { u"Count": 6, u"ScannedCount": 6, u"Items": [ {u"relevant_data": {u"S": u"tete"}}, {u"relevant_data": {u"S": u"tata"}}, {u"relevant_data": {u"S": u"tyty"}}, {u"relevant_data": {u"S": u"tutu"}}, {u"relevant_data": {u"S": u"toto"}}, {u"relevant_data": {u"S": u"titi"}}, ], u"ConsumedCapacityUnits": 1.5, } fields = [u'relevant_data'] db = connect_boto_patch() ret = db.layer1.scan(TABLE_NAME, None, fields) self.assertEqual(expected, ret) # No need to test all conditions/type mismatch as they are unit tested def test_scan_condition_filter_fields_in(self): from ddbmock import connect_boto_patch from ddbmock.database.db import dynamodb expected = { u"Count": 3, u"ScannedCount": 6, u"Items": [ {u"relevant_data": {u"S": u"tata"}}, {u"relevant_data": {u"S": u"toto"}}, {u"relevant_data": {u"S": u"titi"}}, ], u"ConsumedCapacityUnits": 1.5, } conditions = { "relevant_data": { "AttributeValueList": [{"S":"toto"},{"S":"titi"},{"S":"tata"}], "ComparisonOperator": "IN", } } fields = [u'relevant_data'] db = connect_boto_patch() ret = db.layer1.scan(TABLE_NAME, conditions, fields) self.assertEqual(expected, ret) def test_scan_condition_filter_fields_contains(self): from ddbmock import connect_boto_patch from ddbmock.database.db import dynamodb expected = { u"Count": 1, u"ScannedCount": 6, u"Items": [ {u"relevant_data": {u"S": u"toto"}}, ], u"ConsumedCapacityUnits": 1.5, } conditions = { "relevant_data": { "AttributeValueList": [{"S":"to"}], "ComparisonOperator": "CONTAINS", }, } fields = [u'relevant_data'] db = connect_boto_patch() ret = db.layer1.scan(TABLE_NAME, conditions, fields) self.assertEqual(expected, ret) def test_scan_filter_ghost_fields(self): from ddbmock import connect_boto_patch from ddbmock.database.db import dynamodb expected = { u"Count": 0, u"ScannedCount": 6, u"Items": [], u"ConsumedCapacityUnits": 1.5, } conditions = { "ghost field": { "AttributeValueList": [{"N":"123"}], "ComparisonOperator": "LT", }, } fields = [u'relevant_data'] db = connect_boto_patch() ret = db.layer1.scan(TABLE_NAME, conditions, fields) self.assertEqual(expected, ret) def test_scan_validation_error(self): from ddbmock import connect_boto_patch from ddbmock.database.db import dynamodb from boto.dynamodb.exceptions import DynamoDBValidationError expected = { u"Count": 1, u"ScannedCount": 6, u"Items": [ {u"relevant_data": {u"S": u"toto"}}, ], u"ConsumedCapacityUnits": 1.5, } conditions = { "relevant_data": { "AttributeValueList": [{"S":"to"},{"S":"ta"}], "ComparisonOperator": "CONTAINS", } } fields = [u'relevant_data'] db = connect_boto_patch() self.assertRaises(DynamoDBValidationError, db.layer1.scan, TABLE_NAME, conditions, fields )
class TestBatchWriteItem(unittest.TestCase): def setUp(self): from ddbmock.database.db import dynamodb from ddbmock.database.table import Table from ddbmock.database.key import PrimaryKey dynamodb.hard_reset() hash_key = PrimaryKey(TABLE_HK_NAME, TABLE_HK_TYPE) range_key = PrimaryKey(TABLE_RK_NAME, TABLE_RK_TYPE) self.t1 = Table(TABLE_NAME1, TABLE_RT, TABLE_WT, hash_key, range_key) self.t2 = Table(TABLE_NAME2, TABLE_RT, TABLE_WT, hash_key, None) dynamodb.data[TABLE_NAME1] = self.t1 dynamodb.data[TABLE_NAME2] = self.t2 self.t1.put(ITEM1, {}) self.t2.put(ITEM4, {}) def tearDown(self): from ddbmock.database.db import dynamodb from ddbmock import clean_boto_patch dynamodb.hard_reset() clean_boto_patch() def test_batch_write_item_nominal(self): from ddbmock import connect_boto_patch from ddbmock.database.db import dynamodb db = connect_boto_patch() expected = { "Responses": { TABLE_NAME1: { "ConsumedCapacityUnits": 3 }, TABLE_NAME2: { "ConsumedCapacityUnits": 2 } } } ret = db.layer1.batch_write_item({ TABLE_NAME1: [ { u"DeleteRequest": { u"Key": { u"HashKeyElement": {TABLE_HK_TYPE: HK_VALUE1}, u"RangeKeyElement": {TABLE_RK_TYPE: RK_VALUE1}, }, }, }, {u"PutRequest": {u"Item": ITEM2}}, {u"PutRequest": {u"Item": ITEM3}}, ], TABLE_NAME2: [ { u"DeleteRequest": { u"Key": { u"HashKeyElement": {TABLE_HK_TYPE: HK_VALUE2}, }, }, }, {u"PutRequest": {u"Item":ITEM5}}, ], }) self.assertEqual(expected, ret) def test_batch_write_item_table_404(self): from ddbmock import connect_boto_patch from ddbmock.database.db import dynamodb from boto.exception import BotoServerError db = connect_boto_patch() self.assertRaises(BotoServerError, db.layer1.batch_write_item, { TABLE_NAME_404: [ { u"DeleteRequest": { u"Key": { u"HashKeyElement": {TABLE_HK_TYPE: HK_VALUE1}, u"RangeKeyElement": {TABLE_RK_TYPE: RK_VALUE1}, }, }, }, {u"PutRequest": {u"Item": ITEM2}}, {u"PutRequest": {u"Item": ITEM3}}, ], TABLE_NAME2: [ { u"DeleteRequest": { u"Key": { u"HashKeyElement": {TABLE_HK_TYPE: HK_VALUE2}, }, }, }, {u"PutRequest": {u"Item":ITEM5}}, ], })