def test_execution_of_if_not_exists_with_non_existing_attribute_should_return_value( table, ): update_expression = "SET a = if_not_exists(b, :val)" update_expression_values = {":val": {"N": "4"}} update_expression_ast = UpdateExpressionParser.make(update_expression) item = Item( hash_key=DynamoType({"S": "id"}), range_key=None, attrs={"id": { "S": "1" }}, ) validated_ast = UpdateExpressionValidator( update_expression_ast, expression_attribute_names=None, expression_attribute_values=update_expression_values, item=item, table=table, ).validate() UpdateExpressionExecutor(validated_ast, item, None).execute() expected_item = Item( hash_key=DynamoType({"S": "id"}), range_key=None, attrs={ "id": { "S": "1" }, "a": { "N": "4" } }, ) assert expected_item == item
def test_execution_of_add_set_to_a_number(table): update_expression = "add s :value" update_expression_ast = UpdateExpressionParser.make(update_expression) item = Item( hash_key=DynamoType({"S": "id"}), hash_key_type="TYPE", range_key=None, range_key_type=None, attrs={ "id": { "S": "foo2" }, "s": { "N": "5" }, }, ) try: validated_ast = UpdateExpressionValidator( update_expression_ast, expression_attribute_names=None, expression_attribute_values={ ":value": { "SS": ["s1"] } }, item=item, table=table, ).validate() UpdateExpressionExecutor(validated_ast, item, None).execute() expected_item = Item( hash_key=DynamoType({"S": "id"}), hash_key_type="TYPE", range_key=None, range_key_type=None, attrs={ "id": { "S": "foo2" }, "s": { "N": "15" } }, ) assert expected_item == item assert False except IncorrectDataType: assert True
def test_validation_of_if_not_exists_not_existing_value(): update_expression = "SET a = if_not_exists(b, a)" update_expression_ast = UpdateExpressionParser.make(update_expression) item = Item( hash_key=DynamoType({"S": "id"}), hash_key_type="TYPE", range_key=None, range_key_type=None, attrs={ "id": { "S": "1" }, "a": { "S": "A" } }, ) validated_ast = UpdateExpressionValidator( update_expression_ast, expression_attribute_names=None, expression_attribute_values=None, item=item, ).validate() dynamo_value = get_set_action_value(validated_ast) assert dynamo_value == DynamoType({"S": "A"})
def test_validation_of_update_expression_with_keyword(table): try: update_expression = "SET myNum = path + :val" update_expression_values = {":val": {"N": "3"}} update_expression_ast = UpdateExpressionParser.make(update_expression) item = Item( hash_key=DynamoType({"S": "id"}), hash_key_type="TYPE", range_key=None, range_key_type=None, attrs={ "id": { "S": "1" }, "path": { "N": "3" } }, ) UpdateExpressionValidator( update_expression_ast, expression_attribute_names=None, expression_attribute_values=update_expression_values, item=item, table=table, ).validate() assert False, "No exception raised" except AttributeIsReservedKeyword as e: assert e.keyword == "path"
def test_valid_update_expression(table): update_expression = "set forum_name=:NewName, forum_type=:NewType" update_expression_values = { ":NewName": { "S": "AmazingForum" }, ":NewType": { "S": "BASIC" }, } update_expression_ast = UpdateExpressionParser.make(update_expression) item = Item( hash_key=DynamoType({"S": "forum_name"}), hash_key_type="TYPE", range_key=DynamoType({"S": "forum_type"}), range_key_type="TYPE", attrs={"forum_name": { "S": "hello" }}, ) UpdateExpressionValidator( update_expression_ast, expression_attribute_names=None, expression_attribute_values=update_expression_values, item=item, table=table, ).validate()
def test_execution_of_delete_element_from_a_string_attribute(): """A delete statement must use a value of type SS in order to delete elements from a set.""" update_expression = "delete s :value" update_expression_ast = UpdateExpressionParser.make(update_expression) item = Item( hash_key=DynamoType({"S": "id"}), hash_key_type="TYPE", range_key=None, range_key_type=None, attrs={ "id": { "S": "foo2" }, "s": { "S": "5" }, }, ) try: validated_ast = UpdateExpressionValidator( update_expression_ast, expression_attribute_names=None, expression_attribute_values={ ":value": { "SS": ["value2"] } }, item=item, ).validate() UpdateExpressionExecutor(validated_ast, item, None).execute() assert False, "Must raise exception" except IncorrectDataType: assert True
def test_validation_of_update_expression_with_attribute_that_does_not_exist_in_item( table, ): """ When an update expression tries to get an attribute that does not exist it must throw the appropriate exception. An error occurred (ValidationException) when calling the UpdateItem operation: The provided expression refers to an attribute that does not exist in the item """ try: update_expression = "SET a = nonexistent" update_expression_ast = UpdateExpressionParser.make(update_expression) item = Item( hash_key=DynamoType({"S": "id"}), hash_key_type="TYPE", range_key=None, range_key_type=None, attrs={ "id": { "S": "1" }, "path": { "N": "3" } }, ) UpdateExpressionValidator( update_expression_ast, expression_attribute_names=None, expression_attribute_values=None, item=item, table=table, ).validate() assert False, "No exception raised" except AttributeDoesNotExist: assert True
def test_validation_of_if_not_exists_with_existing_attribute_should_return_value( table): update_expression = "SET a = if_not_exists(b, :val)" update_expression_values = {":val": {"N": "4"}} update_expression_ast = UpdateExpressionParser.make(update_expression) item = Item( hash_key=DynamoType({"S": "id"}), hash_key_type="TYPE", range_key=None, range_key_type=None, attrs={ "id": { "S": "1" }, "b": { "N": "3" } }, ) validated_ast = UpdateExpressionValidator( update_expression_ast, expression_attribute_names=None, expression_attribute_values=update_expression_values, item=item, table=table, ).validate() dynamo_value = get_set_action_value(validated_ast) assert dynamo_value == DynamoType({"N": "3"})
def test_execution_of__delete_element_from_set_invalid_value( expression_attribute_values, unexpected_data_type): """A delete statement must use a value of type SS in order to delete elements from a set.""" update_expression = "delete s :value" update_expression_ast = UpdateExpressionParser.make(update_expression) item = Item( hash_key=DynamoType({"S": "id"}), hash_key_type="TYPE", range_key=None, range_key_type=None, attrs={ "id": { "S": "foo2" }, "s": { "SS": ["value1", "value2", "value3"] }, }, ) try: validated_ast = UpdateExpressionValidator( update_expression_ast, expression_attribute_names=None, expression_attribute_values=expression_attribute_values, item=item, ).validate() UpdateExpressionExecutor(validated_ast, item, None).execute() assert False, "Must raise exception" except IncorrectOperandType as e: assert e.operator_or_function == "operator: DELETE" assert e.operand_type == unexpected_data_type
def test_validation_of_a_set_statement_with_incorrect_passed_value( update_expression, table ): """ By running permutations it shows that values are replaced prior to resolving attributes. An error occurred (ValidationException) when calling the UpdateItem operation: Invalid UpdateExpression: An expression attribute value used in expression is not defined; attribute value: :val2 """ update_expression_ast = UpdateExpressionParser.make(update_expression) item = Item( hash_key=DynamoType({"S": "id"}), hash_key_type="TYPE", range_key=None, range_key_type=None, attrs={"id": {"S": "1"}, "b": {"N": "3"}}, ) try: UpdateExpressionValidator( update_expression_ast, expression_attribute_names={"#b": "ok"}, expression_attribute_values={":val": {"N": "3"}}, item=item, table=table, ).validate() except ExpressionAttributeValueNotDefined as e: assert e.attribute_value == ":val2"
def test_validation_set_path_does_not_need_to_be_resolvable_when_setting_a_new_attribute( table, ): """If this step just passes we are happy enough""" update_expression = "set d=a" update_expression_ast = UpdateExpressionParser.make(update_expression) item = Item( hash_key=DynamoType({"S": "id"}), hash_key_type="TYPE", range_key=None, range_key_type=None, attrs={ "id": { "S": "foo2" }, "a": { "N": "3" } }, ) validated_ast = UpdateExpressionValidator( update_expression_ast, expression_attribute_names=None, expression_attribute_values=None, item=item, table=table, ).validate() dynamo_value = get_set_action_value(validated_ast) assert dynamo_value == DynamoType({"N": "3"})
def test_sum_with_incompatible_types(table): """ Must error out: Invalid UpdateExpression: Incorrect operand type for operator or function; operator or function: +, operand type: S' Returns: """ try: update_expression = "SET ri = :val + :val2" update_expression_ast = UpdateExpressionParser.make(update_expression) item = Item( hash_key=DynamoType({"S": "id"}), hash_key_type="TYPE", range_key=None, range_key_type=None, attrs={"id": {"S": "1"}, "ri": {"L": [{"S": "i1"}, {"S": "i2"}]}}, ) UpdateExpressionValidator( update_expression_ast, expression_attribute_names=None, expression_attribute_values={":val": {"S": "N"}, ":val2": {"N": "3"}}, item=item, table=table, ).validate() except IncorrectOperandType as e: assert e.operand_type == "S" assert e.operator_or_function == "+"
def test_cannot_index_into_a_string(table): """ Must error out: The document path provided in the update expression is invalid for update' """ try: update_expression = "set itemstr[1]=:Item" update_expression_ast = UpdateExpressionParser.make(update_expression) item = Item( hash_key=DynamoType({"S": "id"}), hash_key_type="TYPE", range_key=None, range_key_type=None, attrs={"id": {"S": "foo2"}, "itemstr": {"S": "somestring"}}, ) UpdateExpressionValidator( update_expression_ast, expression_attribute_names=None, expression_attribute_values={":Item": {"S": "string_update"}}, item=item, table=table, ).validate() assert False, "Must raise exception" except InvalidUpdateExpressionInvalidDocumentPath: assert True
def test_validation_of_update_expression_with_attribute_name_that_is_not_defined( update_expression, table, ): """ When an update expression tries to get an attribute name that is not provided it must throw an exception. An error occurred (ValidationException) when calling the UpdateItem operation: Invalid UpdateExpression: An expression attribute name used in the document path is not defined; attribute name: #c """ try: update_expression_ast = UpdateExpressionParser.make(update_expression) item = Item( hash_key=DynamoType({"S": "id"}), hash_key_type="TYPE", range_key=None, range_key_type=None, attrs={"id": {"S": "1"}, "path": {"N": "3"}}, ) UpdateExpressionValidator( update_expression_ast, expression_attribute_names={"#b": "ok"}, expression_attribute_values=None, item=item, table=table, ).validate() assert False, "No exception raised" except ExpressionAttributeNameNotDefined as e: assert e.not_defined_attribute_name == "#c"
def test_validation_of_sum_operation(table): update_expression = "SET a = a + b" update_expression_ast = UpdateExpressionParser.make(update_expression) item = Item( hash_key=DynamoType({"S": "id"}), hash_key_type="TYPE", range_key=None, range_key_type=None, attrs={ "id": { "S": "1" }, "a": { "N": "3" }, "b": { "N": "4" } }, ) validated_ast = UpdateExpressionValidator( update_expression_ast, expression_attribute_names=None, expression_attribute_values=None, item=item, table=table, ).validate() dynamo_value = get_set_action_value(validated_ast) assert dynamo_value == DynamoType({"N": "7"})
def test_validation_set_path_does_not_need_to_be_resolvable_but_must_be_creatable_when_setting_a_new_attribute( table, ): try: update_expression = "set d.e=a" update_expression_ast = UpdateExpressionParser.make(update_expression) item = Item( hash_key=DynamoType({"S": "id"}), hash_key_type="TYPE", range_key=None, range_key_type=None, attrs={ "id": { "S": "foo2" }, "a": { "N": "3" } }, ) UpdateExpressionValidator( update_expression_ast, expression_attribute_names=None, expression_attribute_values=None, item=item, table=table, ).validate() assert False, "Must raise exception" except InvalidUpdateExpressionInvalidDocumentPath: assert True
def test_validation_of_if_not_exists_not_existing_invalid_replace_value(table): try: update_expression = "SET a = if_not_exists(b, a.c)" update_expression_ast = UpdateExpressionParser.make(update_expression) item = Item( hash_key=DynamoType({"S": "id"}), hash_key_type="TYPE", range_key=None, range_key_type=None, attrs={ "id": { "S": "1" }, "a": { "S": "A" } }, ) UpdateExpressionValidator( update_expression_ast, expression_attribute_names=None, expression_attribute_values=None, item=item, table=table, ).validate() assert False, "No exception raised" except AttributeDoesNotExist: assert True
def test_execution_of_if_not_exists_with_existing_attribute_should_return_attribute( ): update_expression = "SET a = if_not_exists(b, a)" update_expression_ast = UpdateExpressionParser.make(update_expression) item = Item( hash_key=DynamoType({"S": "id"}), hash_key_type="TYPE", range_key=None, range_key_type=None, attrs={ "id": { "S": "1" }, "a": { "S": "A" }, "b": { "S": "B" } }, ) validated_ast = UpdateExpressionValidator( update_expression_ast, expression_attribute_names=None, expression_attribute_values=None, item=item, ).validate() UpdateExpressionExecutor(validated_ast, item, None).execute() expected_item = Item( hash_key=DynamoType({"S": "id"}), hash_key_type="TYPE", range_key=None, range_key_type=None, attrs={ "id": { "S": "1" }, "a": { "S": "B" }, "b": { "S": "B" } }, ) assert expected_item == item
def test_execution_of_sum_operation(): update_expression = "SET a = a + b" update_expression_ast = UpdateExpressionParser.make(update_expression) item = Item( hash_key=DynamoType({"S": "id"}), hash_key_type="TYPE", range_key=None, range_key_type=None, attrs={ "id": { "S": "1" }, "a": { "N": "3" }, "b": { "N": "4" } }, ) validated_ast = UpdateExpressionValidator( update_expression_ast, expression_attribute_names=None, expression_attribute_values=None, item=item, ).validate() UpdateExpressionExecutor(validated_ast, item, None).execute() expected_item = Item( hash_key=DynamoType({"S": "id"}), hash_key_type="TYPE", range_key=None, range_key_type=None, attrs={ "id": { "S": "1" }, "a": { "N": "7" }, "b": { "N": "4" } }, ) assert expected_item == item
def test_execution_of_add_to_a_set(table): update_expression = "ADD s :value" update_expression_ast = UpdateExpressionParser.make(update_expression) item = Item( hash_key=DynamoType({"S": "id"}), hash_key_type="TYPE", range_key=None, range_key_type=None, attrs={ "id": { "S": "foo2" }, "s": { "SS": ["value1", "value2", "value3"] }, }, ) validated_ast = UpdateExpressionValidator( update_expression_ast, expression_attribute_names=None, expression_attribute_values={ ":value": { "SS": ["value2", "value5"] } }, item=item, table=table, ).validate() UpdateExpressionExecutor(validated_ast, item, None).execute() expected_item = Item( hash_key=DynamoType({"S": "id"}), hash_key_type="TYPE", range_key=None, range_key_type=None, attrs={ "id": { "S": "foo2" }, "s": { "SS": ["value1", "value2", "value3", "value5"] }, }, ) assert expected_item == item
def test_execution_of_add_number(): update_expression = "add s :value" update_expression_ast = UpdateExpressionParser.make(update_expression) item = Item( hash_key=DynamoType({"S": "id"}), hash_key_type="TYPE", range_key=None, range_key_type=None, attrs={ "id": { "S": "foo2" }, "s": { "N": "5" }, }, ) validated_ast = UpdateExpressionValidator( update_expression_ast, expression_attribute_names=None, expression_attribute_values={ ":value": { "N": "10" } }, item=item, ).validate() UpdateExpressionExecutor(validated_ast, item, None).execute() expected_item = Item( hash_key=DynamoType({"S": "id"}), hash_key_type="TYPE", range_key=None, range_key_type=None, attrs={ "id": { "S": "foo2" }, "s": { "N": "15" } }, ) assert expected_item == item
def test_execution_of_remove(table): update_expression = "Remove a" update_expression_ast = UpdateExpressionParser.make(update_expression) item = Item( hash_key=DynamoType({"S": "id"}), range_key=None, attrs={ "id": { "S": "1" }, "a": { "N": "3" }, "b": { "N": "4" } }, ) validated_ast = UpdateExpressionValidator( update_expression_ast, expression_attribute_names=None, expression_attribute_values=None, item=item, table=table, ).validate() UpdateExpressionExecutor(validated_ast, item, None).execute() expected_item = Item( hash_key=DynamoType({"S": "id"}), range_key=None, attrs={ "id": { "S": "1" }, "b": { "N": "4" } }, ) assert expected_item == item
def test_validation_homogeneous_list_append_function(table): update_expression = "SET ri = list_append(ri, :vals)" update_expression_ast = UpdateExpressionParser.make(update_expression) item = Item( hash_key=DynamoType({"S": "id"}), hash_key_type="TYPE", range_key=None, range_key_type=None, attrs={ "id": { "S": "1" }, "ri": { "L": [{ "S": "i1" }, { "S": "i2" }] } }, ) validated_ast = UpdateExpressionValidator( update_expression_ast, expression_attribute_names=None, expression_attribute_values={ ":vals": { "L": [{ "S": "i3" }, { "S": "i4" }] } }, item=item, table=table, ).validate() dynamo_value = get_set_action_value(validated_ast) assert dynamo_value == DynamoType( {"L": [{ "S": "i1" }, { "S": "i2" }, { "S": "i3" }, { "S": "i4" }]})
def test_validation_of_empty_string_key_val(table): with pytest.raises(EmptyKeyAttributeException): update_expression = "set forum_name=:NewName" update_expression_values = {":NewName": {"S": ""}} update_expression_ast = UpdateExpressionParser.make(update_expression) item = Item( hash_key=DynamoType({"S": "forum_name"}), hash_key_type="TYPE", range_key=None, range_key_type=None, attrs={"forum_name": {"S": "hello"}}, ) UpdateExpressionValidator( update_expression_ast, expression_attribute_names=None, expression_attribute_values=update_expression_values, item=item, table=table, ).validate()
def test_validation_list_append_function_with_non_list_arg(): """ Must error out: Invalid UpdateExpression: Incorrect operand type for operator or function; operator or function: list_append, operand type: S' Returns: """ try: update_expression = "SET ri = list_append(ri, :vals)" update_expression_ast = UpdateExpressionParser.make(update_expression) item = Item( hash_key=DynamoType({"S": "id"}), hash_key_type="TYPE", range_key=None, range_key_type=None, attrs={ "id": { "S": "1" }, "ri": { "L": [{ "S": "i1" }, { "S": "i2" }] } }, ) UpdateExpressionValidator( update_expression_ast, expression_attribute_names=None, expression_attribute_values={ ":vals": { "S": "N" } }, item=item, ).validate() except IncorrectOperandType as e: assert e.operand_type == "S" assert e.operator_or_function == "list_append"
def test_execution_of_remove_in_list(): update_expression = "Remove itemmap.itemlist[1]" update_expression_ast = UpdateExpressionParser.make(update_expression) item = Item( hash_key=DynamoType({"S": "id"}), hash_key_type="TYPE", range_key=None, range_key_type=None, attrs={ "id": { "S": "foo2" }, "itemmap": { "M": { "itemlist": { "L": [ { "M": { "foo00": { "S": "bar1" }, "foo01": { "S": "bar2" } } }, { "M": { "foo10": { "S": "bar1" }, "foo11": { "S": "bar2" } } }, ] } } }, }, ) validated_ast = UpdateExpressionValidator( update_expression_ast, expression_attribute_names=None, expression_attribute_values=None, item=item, ).validate() UpdateExpressionExecutor(validated_ast, item, None).execute() expected_item = Item( hash_key=DynamoType({"S": "id"}), hash_key_type="TYPE", range_key=None, range_key_type=None, attrs={ "id": { "S": "foo2" }, "itemmap": { "M": { "itemlist": { "L": [ { "M": { "foo00": { "S": "bar1" }, "foo01": { "S": "bar2" } } }, ] } } }, }, ) assert expected_item == item
def test_execution_of_delete_element_from_set(table, attr_name): expression_attribute_names = {"#placeholder": "s"} update_expression = "delete {} :value".format(attr_name) update_expression_ast = UpdateExpressionParser.make(update_expression) item = Item( hash_key=DynamoType({"S": "id"}), range_key=None, attrs={ "id": { "S": "foo2" }, "s": { "SS": ["value1", "value2", "value3"] } }, ) validated_ast = UpdateExpressionValidator( update_expression_ast, expression_attribute_names=expression_attribute_names, expression_attribute_values={ ":value": { "SS": ["value2", "value5"] } }, item=item, table=table, ).validate() UpdateExpressionExecutor(validated_ast, item, expression_attribute_names).execute() expected_item = Item( hash_key=DynamoType({"S": "id"}), range_key=None, attrs={ "id": { "S": "foo2" }, "s": { "SS": ["value1", "value3"] }, }, ) assert expected_item == item # delete last elements update_expression = "delete {} :value".format(attr_name) update_expression_ast = UpdateExpressionParser.make(update_expression) validated_ast = UpdateExpressionValidator( update_expression_ast, expression_attribute_names=expression_attribute_names, expression_attribute_values={ ":value": { "SS": ["value1", "value3"] } }, item=item, table=table, ).validate() UpdateExpressionExecutor(validated_ast, item, expression_attribute_names).execute() expected_item = Item( hash_key=DynamoType({"S": "id"}), range_key=None, attrs={"id": { "S": "foo2" }}, ) assert expected_item == item