def test_always_fails_and_slow(self): self.app = helpers.makeTestApp(user = "******") from ddbmock import connect_boto_patch, config delay = 1 config.config["always_fail_and_slow"]["DELAY_OPERATIONS"] = delay connect_boto_patch() expected = { u'message': u"The server encountered an internal error trying to fulfill the request", u'__type': u'com.amazonaws.dynamodb.v20120810#InternalServerError' } request = {} HEADERS = { 'x-amz-target': 'dynamodb_20111205.list_tables', 'content-type': 'application/x-amz-json-1.0', } start = time() res = self.app.post_json('/', request, headers=HEADERS, status=500) end = time() self.assertEqual(expected, json.loads(res.body)) self.assertEqual('application/x-amz-json-1.0; charset=UTF-8', res.headers['Content-Type']) self.assertAlmostEqual(delay, end-start, delta = 0.1)
def test_intermittent_failure(self): self.app = helpers.makeTestApp(user = "******") from ddbmock import connect_boto_patch, config connect_boto_patch() request = {} HEADERS = { 'x-amz-target': 'dynamodb_20111205.list_tables', 'content-type': 'application/x-amz-json-1.0', } for _ in range(3): expected = { u'TableNames': [] } 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']) expected = { u'message': u"The server encountered an internal error trying to fulfill the request", u'__type': u'com.amazonaws.dynamodb.v20120810#InternalServerError' } res = self.app.post_json('/', request, headers=HEADERS, status=500) self.assertEqual(expected, json.loads(res.body)) self.assertEqual('application/x-amz-json-1.0; charset=UTF-8', res.headers['Content-Type'])
def test_invalid_user(self): self.app = helpers.makeTestApp(user = "******") from ddbmock import connect_boto_patch connect_boto_patch() expected = { u'message': u"Can't find invalid_user in users", u'__type': u'com.amazonaws.dynamodb.v20120810#AccessDeniedException' } res = self.app.post_json('/', {}, 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'])
def test_list_tables(self): from ddbmock import connect_boto_patch connect_boto_patch() request = {} expected = { "TableNames": [TABLE_NAME1, TABLE_NAME2], } 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 describe_table_404(self): from ddbmock import connect_boto_patch db = connect_boto_patch() self.assertRaises(DDBValidationErr, db.get_table, TABLE_NAME_404, )
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 )
def setUp(self): from ddbmock import connect_boto_patch from ddbmock.database.db import dynamodb from ddbmock.database.table import Table from ddbmock.database.key import PrimaryKey # Do a full database wipe dynamodb.hard_reset() # Instanciate the keys hash_key = PrimaryKey(self.TABLE_HK_NAME, self.TABLE_HK_TYPE) # Create a test table new_table = Table(self.TABLE_NAME, self.TABLE_RT, self.TABLE_WT, hash_key, None) # Very important: register the table in the DB dynamodb.data[self.TABLE_NAME] = new_table # Create the database connection ie: patch boto self.db = connect_boto_patch() self.table = self.db.get_table(self.TABLE_NAME) self.sns_conn = boto.connect_sns() self.memon = MEMon() self.memon.debug = False self.memon.table = self.table self.memon.sns_conn = self.sns_conn self.memon.server_time = False
def test_create_table_hash(self): from ddbmock import connect_boto_patch from ddbmock.database.db import dynamodb db = connect_boto_patch() table = db.create_table( name=TABLE_NAME2, schema=db.create_schema(**TABLE_SCHEMA2), read_units=TABLE_RT, write_units=TABLE_WT, ) self.assertEqual(TABLE_NAME2, table.name) self.assertEqual(TABLE_RT, table.read_units) self.assertEqual(TABLE_WT, table.write_units) self.assertEqual(u'CREATING', table.status) self.assertEqual(TABLE_SCHEMA2['hash_key_name'], table.schema.hash_key_name) self.assertEqual(u'N', table.schema.hash_key_type) self.assertIsNone(table.schema.range_key_name) self.assertIsNone(table.schema.range_key_type) data = dynamodb.data assert TABLE_NAME2 in data table = data[TABLE_NAME2] self.assertEqual(TABLE_NAME2, table.name) self.assertEqual(TABLE_RT, table.rt) self.assertEqual(TABLE_WT, table.wt) self.assertEqual(TABLE_SCHEMA2['hash_key_name'], table.hash_key.name) self.assertEqual(u'N', table.hash_key.typename) self.assertIsNone(table.range_key)
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)
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_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_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_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}}, ], })
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_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_list_tables(self): from ddbmock import connect_boto_patch db = connect_boto_patch() expected = [TABLE_NAME1, TABLE_NAME2] self.assertEqual(expected, db.list_tables())
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_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_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_create_table_hash_range(self): from ddbmock import connect_boto_patch from ddbmock.database.db import dynamodb db = connect_boto_patch() table = db.create_table( name=TABLE_NAME1, schema=[x.schema() for x in TABLE_SCHEMA1], read_units=TABLE_RT, write_units=TABLE_WT, ) self.assertEqual(TABLE_NAME1, table.name) self.assertEqual(TABLE_RT, table.read_units) self.assertEqual(TABLE_WT, table.write_units) self.assertEqual(u'CREATING', table.status) self.assertEqual(TABLE_SCHEMA1['hash_key_name'], table.schema.hash_key_name) self.assertEqual(TABLE_SCHEMA1['range_key_name'], table.schema.range_key_name) self.assertEqual(u'N', table.schema.hash_key_type) self.assertEqual(u'S', table.schema.range_key_type) data = dynamodb.data assert TABLE_NAME1 in data table = data[TABLE_NAME1] self.assertEqual(TABLE_NAME1, table.name) self.assertEqual(TABLE_RT, table.rt) self.assertEqual(TABLE_WT, table.wt) self.assertEqual(TABLE_SCHEMA1['hash_key_name'], table.hash_key.name) self.assertEqual(TABLE_SCHEMA1['range_key_name'], table.range_key.name) self.assertEqual(u'N', table.hash_key.typename) self.assertEqual(u'S', table.range_key.typename)
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_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_connect_boto_patch_patch(self): from ddbmock import connect_boto_patch, real_boto, layer1_mock_init from ddbmock.router.boto import boto_router db = connect_boto_patch() self.assertEqual(boto_router, db.layer1.make_request.im_func) self.assertEqual(layer1_mock_init, db.layer1.__init__.im_func) self.assertNotEqual(real_boto['Layer1.make_request'], boto_router) self.assertNotEqual(real_boto['Layer1.__init__'], layer1_mock_init) # do it twice to make sure it is safe db = connect_boto_patch() self.assertEqual(boto_router, db.layer1.make_request.im_func) self.assertEqual(layer1_mock_init, db.layer1.__init__.im_func) self.assertNotEqual(real_boto['Layer1.make_request'], boto_router) self.assertNotEqual(real_boto['Layer1.__init__'], layer1_mock_init)
def test_readonly_user_can(self, name): self.app = helpers.makeTestApp(user = "******") from ddbmock import connect_boto_patch connect_boto_patch() HEADERS = { 'x-amz-target': 'dynamodb_20111205.%s'% name, 'content-type': 'application/x-amz-json-1.0', } res = self.app.post_json('/', {}, headers=HEADERS, status=[200,400]) ret = json.loads(res.body) self.assertEqual('application/x-amz-json-1.0; charset=UTF-8', res.headers['Content-Type']) # Because no arguments, we either get a 200 for things that require no args, or a 400 for things that need args if res.status_code == 200: return self.assertIn("__type", ret) self.assertEqual(u'com.amazonaws.dynamodb.v20120810#ValidationException', ret["__type"])
def test_delete_404(self): from ddbmock import connect_boto_patch from boto.exception import BotoServerError db = connect_boto_patch() self.assertRaises(BotoServerError, db.layer1.delete_table, TABLE_NAME_404, )
def test_put_boto_intergration(self): # This item comes directly from boto intergration test suite # we do no assertion. It should "just work" from ddbmock import connect_boto_patch from ddbmock.database.db import dynamodb db = connect_boto_patch() db.layer1.put_item(TABLE_NAME2, ITEM_REGRESION)
def test_connect_boto_patch_patch(self): from ddbmock import connect_boto_patch, real_boto, noop from ddbmock.router.boto import boto_router db = connect_boto_patch() self.assertEqual(boto_router, db.layer1.make_request.im_func) self.assertEqual(noop, db.layer1.__init__.im_func) self.assertNotEqual(real_boto['Layer1.make_request'], boto_router) self.assertNotEqual(real_boto['Layer1.__init__'], noop) # do it twice to make sure it is safe db = connect_boto_patch() self.assertEqual(boto_router, db.layer1.make_request.im_func) self.assertEqual(noop, db.layer1.__init__.im_func) self.assertNotEqual(real_boto['Layer1.make_request'], boto_router) self.assertNotEqual(real_boto['Layer1.__init__'], noop)
def test_put_h_404(self): from ddbmock import connect_boto_patch from ddbmock.database.db import dynamodb from boto.exception import DynamoDBResponseError db = connect_boto_patch() self.assertRaisesRegexp(DynamoDBResponseError, 'ResourceNotFoundException', db.layer1.put_item, TABLE_NAME_404, ITEM3)
def test_delete(self): from ddbmock import connect_boto_patch from ddbmock.database.db import dynamodb db = connect_boto_patch() db.layer1.delete_table(TABLE_NAME) data = dynamodb.data self.assertEqual("DELETING", data[TABLE_NAME].status)
def test_put_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() self.assertRaises(DynamoDBValidationError, db.layer1.put_item, TABLE_NAME2, ITEM5)
def test_delete_CREATING_status(self): from ddbmock import connect_boto_patch from ddbmock.database.db import dynamodb from boto.exception import DynamoDBResponseError db = connect_boto_patch() self.assertRaisesRegexp(DynamoDBResponseError, 'ResourceInUseException', db.layer1.delete_table, TABLE_NAME2)