Repository: getmoto/moto
Pull request #7684 (2024-05-11 19:41:10)

DynamoDB: update_item() now throws the appriopriate exception when adding an empty set
Partial fix for #7595 

codecov[bot]: ## [Codecov](https://app.codecov.io/gh/getmoto/moto/pull/7684?dropdown=coverage&src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=getmoto) Report
All modified and coverable lines are covered by tests :white_check_mark:
> Project coverage is 94.32%. Comparing base [(`cf8e560`)](https://app.codecov.io/gh/getmoto/moto/commit/cf8e560e4bfc503975dbf72d2e0a1e5af55f27fa?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=getmoto) to head [(`af994cf`)](https://app.codecov.io/gh/getmoto/moto/pull/7684?dropdown=coverage&src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=getmoto).


<details><summary>Additional details and impacted files</summary>


```diff
@@           Coverage Diff           @@
##           master    #7684   +/-   ##
=======================================
  Coverage   94.32%   94.32%           
=======================================
  Files        1087     1087           
  Lines       92690    92692    +2     
=======================================
+ Hits        87432    87435    +3     
+ Misses       5258     5257    -1     
```

| [Flag](https://app.codecov.io/gh/getmoto/moto/pull/7684/flags?src=pr&el=flags&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=getmoto) | Coverage Δ | |
|---|---|---|
| [servertests](https://app.codecov.io/gh/getmoto/moto/pull/7684/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=getmoto) | `29.41% <50.00%> (+<0.01%)` | :arrow_up: |
| [unittests](https://app.codecov.io/gh/getmoto/moto/pull/7684/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=getmoto) | `94.29% <100.00%> (+<0.01%)` | :arrow_up: |

Flags with carried forward coverage won't be shown. [Click here](https://docs.codecov.io/docs/carryforward-flags?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=getmoto#carryforward-flags-in-the-pull-request-comment) to find out more.


</details>

[:umbrella: View full report in Codecov by Sentry](https://app.codecov.io/gh/getmoto/moto/pull/7684?dropdown=coverage&src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=getmoto).   
:loudspeaker: Have feedback on the report? [Share it here](https://about.codecov.io/codecov-pr-comment-feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=getmoto).


Patch:
diff --git a/moto/dynamodb/parsing/executors.py b/moto/dynamodb/parsing/executors.py
index b6a7c6eef..23aac7d6d 100644
--- a/moto/dynamodb/parsing/executors.py
+++ b/moto/dynamodb/parsing/executors.py
@@ -4,6 +4,7 @@ from typing import Any, Dict, List, Optional, Type, Union
 from moto.dynamodb.exceptions import (
     IncorrectDataType,
     IncorrectOperandType,
+    MockValidationException,
     ProvidedKeyDoesNotExist,
 )
 from moto.dynamodb.models.dynamo_type import (
@@ -226,6 +227,10 @@ class AddExecutor(NodeExecutor):
         value_to_add = self.get_action_value()
         if isinstance(value_to_add, DynamoType):
             if value_to_add.is_set():
+                if len(value_to_add.value) == 0:
+                    raise MockValidationException(
+                        "ExpressionAttributeValues contains invalid value: One or more parameter values were invalid: An string set  may not be empty"
+                    )
                 try:
                     current_string_set = self.get_item_at_end_of_path(item)
                 except ProvidedKeyDoesNotExist:


Test patch:
diff --git a/tests/test_dynamodb/test_dynamodb_update_expressions.py b/tests/test_dynamodb/test_dynamodb_update_expressions.py
index d891f75d8..5115e7168 100644
--- a/tests/test_dynamodb/test_dynamodb_update_expressions.py
+++ b/tests/test_dynamodb/test_dynamodb_update_expressions.py
@@ -2,6 +2,7 @@ from decimal import Decimal
 
 import boto3
 import pytest
+from botocore.exceptions import ClientError
 
 from . import dynamodb_aws_verified
 
@@ -140,3 +141,49 @@ def test_delete_non_existing_item(table_name=None):
         ReturnValues="ALL_NEW",
     )
     assert table.get_item(Key={"pk": name})["Item"] == {"pk": "name"}
+
+
+@pytest.mark.aws_verified
+@dynamodb_aws_verified()
+def test_update_item_add_empty_set(table_name=None):
+    dynamodb = boto3.client("dynamodb", "us-east-1")
+
+    # Add to non-existing attribute
+    dynamodb.put_item(TableName=table_name, Item={"pk": {"S": "foo"}})
+    with pytest.raises(ClientError) as exc:
+        dynamodb.update_item(
+            TableName=table_name,
+            Key={"pk": {"S": "foo"}},
+            UpdateExpression="ADD stringset :emptySet",
+            ExpressionAttributeValues={":emptySet": {"SS": ()}},
+        )
+    err = exc.value.response["Error"]
+    assert err["Code"] == "ValidationException"
+    assert (
+        err["Message"]
+        == "ExpressionAttributeValues contains invalid value: One or more parameter values were invalid: An string set  may not be empty"
+    )
+
+    assert dynamodb.scan(TableName=table_name)["Items"] == [{"pk": {"S": "foo"}}]
+
+    # Still not allowed when the attribute exists
+    dynamodb.put_item(
+        TableName=table_name, Item={"pk": {"S": "foo"}, "stringset": {"SS": ("item1",)}}
+    )
+    with pytest.raises(ClientError) as exc:
+        dynamodb.update_item(
+            TableName=table_name,
+            Key={"pk": {"S": "foo"}},
+            UpdateExpression="ADD stringset :emptySet",
+            ExpressionAttributeValues={":emptySet": {"SS": ()}},
+        )
+    err = exc.value.response["Error"]
+    assert err["Code"] == "ValidationException"
+    assert (
+        err["Message"]
+        == "ExpressionAttributeValues contains invalid value: One or more parameter values were invalid: An string set  may not be empty"
+    )
+
+    assert dynamodb.scan(TableName=table_name)["Items"] == [
+        {"pk": {"S": "foo"}, "stringset": {"SS": ["item1"]}}
+    ]