def test_nested_selectors_in_update_expression_should_fail_at_nesting(): update_expression = "SET a [ [2] ]. b = 5" try: UpdateExpressionParser.make(update_expression) assert False, "Exception not raised correctly" except InvalidTokenException as te: assert te.token == "[" assert te.near == "[ [2"
def test_expression_tokenizer_set_closing_followed_by_numeric_literal(): set_action = "SET ) 3" try: UpdateExpressionParser.make(set_action) assert False, "Exception not raised correctly" except InvalidTokenException as te: assert te.token == ")" assert te.near == "SET ) 3"
def test_expression_tokenizer_only_numeric_literal(): set_action = "2" try: UpdateExpressionParser.make(set_action) assert False, "Exception not raised correctly" except InvalidTokenException as te: assert te.token == "2" assert te.near == "2"
def test_update_expression_number_in_selector_cannot_be_splite(): update_expression = "SET a [2 1]. b = 5" try: UpdateExpressionParser.make(update_expression) assert False, "Exception not raised correctly" except InvalidTokenException as te: assert te.token == "1" assert te.near == "2 1]"
def test_expression_tokenizer_set_closing_round_bracket(): set_action = "SET )" try: UpdateExpressionParser.make(set_action) assert False, "Exception not raised correctly" except InvalidTokenException as te: assert te.token == ")" assert te.near == "SET )"
def test_expression_if_not_exists_is_not_valid_in_remove_statement(): set_action = "REMOVE if_not_exists(a,b)" try: UpdateExpressionParser.make(set_action) assert False, "Exception not raised correctly" except InvalidTokenException as te: assert te.token == "(" assert te.near == "if_not_exists(a"
def test_expression_tokenizer_2_different_operators_back_to_back(): set_action = "SET MyStr = NoExist + - :_val " try: UpdateExpressionParser.make(set_action) assert False, "Exception not raised correctly" except InvalidTokenException as te: assert te.token == "-" assert te.near == "+ - :_val"
def test_expression_tokenizer_numeric_literal_unclosed_square_bracket(): set_action = "SET MyStr[ 3" try: UpdateExpressionParser.make(set_action) assert False, "Exception not raised correctly" except InvalidTokenException as te: assert te.token == "<EOF>" assert te.near == "3"
def test_update_expression_cannot_have_successive_attributes(): update_expression = "SET #a a = 5" try: UpdateExpressionParser.make(update_expression) assert False, "Exception not raised correctly" except InvalidTokenException as te: assert te.token == "a" assert te.near == "#a a ="
def test_expression_tokenizer_multi_number_numeric_literal_in_expression(): set_action = "SET attrName = 34" try: UpdateExpressionParser.make(set_action) assert False, "Exception not raised correctly" except InvalidTokenException as te: assert te.token == "34" assert te.near == "= 34"
def test_update_expression_add_does_not_allow_attribute_foobar_after_value(): add_expr = "ADD attr :val foobar" try: UpdateExpressionParser.make(add_expr) assert False, "Exception not raised correctly" except InvalidTokenException as te: assert te.token == "foobar" assert te.near == ":val foobar"
def test_update_expression_starts_with_keyword_reset_followed_by_identifier(): update_expression = "RESET NonExistent" try: UpdateExpressionParser.make(update_expression) assert False, "Exception not raised correctly" except InvalidTokenException as te: assert te.token == "RESET" assert te.near == "RESET NonExistent"
def test_update_expression_with_only_keyword_reset(): update_expression = "RESET" try: UpdateExpressionParser.make(update_expression) assert False, "Exception not raised correctly" except InvalidTokenException as te: assert te.token == "RESET" assert te.near == "RESET"
def test_expression_tokenizer_unbalanced_square_brackets_only_closing(): set_action = "SET MyStr = ]:_val" try: UpdateExpressionParser.make(set_action) assert False, "Exception not raised correctly" except InvalidTokenException as te: assert te.token == "]" assert te.near == "= ]:_val"
def test_expression_tokenizer_unbalanced_round_brackets_multiple_opening(): set_action = "SET MyStr = (:_val + (:val2" try: UpdateExpressionParser.make(set_action) assert False, "Exception not raised correctly" except InvalidTokenException as te: assert te.token == "<EOF>" assert te.near == ":val2"
def test_update_expression_remove_does_not_allow_operations(): remove_action = "REMOVE NoExist + " try: UpdateExpressionParser.make(remove_action) assert False, "Exception not raised correctly" except InvalidTokenException as te: assert te.token == "+" assert te.near == "NoExist + "
def test_expression_tokenizer_wrong_closing_bracket_with_space(): set_action = "SET MyStr[3 )" try: UpdateExpressionParser.make(set_action) assert False, "Exception not raised correctly" except InvalidTokenException as te: assert te.token == ")" assert te.near == "3 )"
def test_update_expression_path_with_both_attribute_and_attribute_name_should_only_fail_at_numeric_value( ): update_expression = "SET #a.a = 5" try: UpdateExpressionParser.make(update_expression) assert False, "Exception not raised correctly" except InvalidTokenException as te: assert te.token == "5" assert te.near == "= 5"
def test_update_expression_parsing_is_not_keyword_aware(): """path and VALUE are keywords. Yet a token error will be thrown for the numeric literal 1.""" delete_expr = "SET path = VALUE 1" try: UpdateExpressionParser.make(delete_expr) assert False, "Exception not raised correctly" except InvalidTokenException as te: assert te.token == "1" assert te.near == "VALUE 1"
def test_update_nested_expression_should_only_fail_parsing_at_numeric_literal_value( ): update_expression = "SET a . b = 5" try: UpdateExpressionParser.make(update_expression) assert False, "Exception not raised correctly" except InvalidTokenException as te: assert te.token == "5" assert te.near == "= 5"
def test_expression_tokenizer_unbalanced_round_brackets_only_closing_followed_by_other_parts( ): set_action = "SET MyStr = ):_val + :val2" try: UpdateExpressionParser.make(set_action) assert False, "Exception not raised correctly" except InvalidTokenException as te: assert te.token == ")" assert te.near == "= ):_val"
def test_update_expression_delete_does_not_allow_attribute_after_path(): """value here is not really a value since a value starts with a colon (:)""" delete_expr = "DELETE attr val" try: UpdateExpressionParser.make(delete_expr) assert False, "Exception not raised correctly" except InvalidTokenException as te: assert te.token == "val" assert te.near == "attr val"
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"}), range_key=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_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"}), range_key=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_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"}), range_key=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_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"}), range_key=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_valid_update_expression(table): update_expression = "set forum_desc=:Desc, forum_type=:NewType" update_expression_values = { ":Desc": { "S": "AmazingForum" }, ":NewType": { "S": "BASIC" }, } update_expression_ast = UpdateExpressionParser.make(update_expression) item = Item( hash_key=DynamoType({"S": "forum_name"}), range_key=DynamoType({"S": "forum_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_validation_of_subraction_operation(table): update_expression = "SET ri = :val - :val2" 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={ ":val": { "N": "1" }, ":val2": { "N": "3" } }, item=item, table=table, ).validate() dynamo_value = get_set_action_value(validated_ast) assert dynamo_value == DynamoType({"N": "-2"})
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"}), range_key=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_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"}), range_key=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"})