Repository: getmoto/moto

Issue:
DynamoDB: DynamoDB.Client.update_item() Doesn't Work with String Set
**Summary**
`DynamoDB.Client.update_item()` expects `list` as input for String Set according to the [documentation](https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/dynamodb.html#DynamoDB.Client.update_item):
```
SS (list) --
An attribute of type String Set. For example:

"SS": ["Giraffe", "Hippo" ,"Zebra"]
```
However **moto** accepts `set` only for `SS` since this [commit](https://github.com/spulec/moto/commit/a2aefc49b486f8191541f033007b6b3cf81ed723). And the logic is not reachable as **boto3** would fire `ParamValidationError` before reaching **moto**.

**Details**
I tested with the following code snippet.
```
    @mock_dynamodb2
    def test_dynamodb_update_item(self):
        dynamodb = boto3.resource('dynamodb', region_name='eu-west-1')

        table = dynamodb.create_table(
            TableName='test',
            KeySchema=[
                {'AttributeName': 'record_id', 'KeyType': 'HASH'},
            ],
            AttributeDefinitions=[
                {'AttributeName': 'record_id', 'AttributeType': 'S'},
            ],
        )
        table.meta.client.get_waiter('table_exists').wait(TableName='test')
        attribute = {
            'test_field': {
                'Value': {
                    'SS': ['test1', 'test2'],
                },
                'Action': 'PUT'
            }
        }

        boto3.client('dynamodb').update_item(
            TableName='test',
            Key={'record_id': {'S': 'testrecord'}},
            AttributeUpdates=attribute,
        )
```

**Expected Behavior**
Test to proceed without error.

**Actual Behavior**
```
self = <[AttributeError("'DynamoType' object has no attribute 'value'") raised in repr()] DynamoType object at 0x7faa6958da50>
type_as_dict = 'test'

    def __init__(self, type_as_dict):
        if type(type_as_dict) == DynamoType:
            self.type = type_as_dict.type
            self.value = type_as_dict.value
        else:
            self.type = list(type_as_dict)[0]
>           self.value = list(type_as_dict.values())[0]
E           AttributeError: 'str' object has no attribute 'values'
```
Call stack:
```
moto/core/models.py:323: in __call__
    request, request.url, request.headers
moto/core/responses.py:202: in dispatch
    return cls()._dispatch(*args, **kwargs)
moto/core/responses.py:312: in _dispatch
    return self.call_action()
moto/core/utils.py:277: in _wrapper
    response = f(*args, **kwargs)
moto/dynamodb2/responses.py:86: in call_action
    response = getattr(self, endpoint)()
moto/dynamodb2/responses.py:777: in update_item
    condition_expression=condition_expression,
moto/dynamodb2/models/__init__.py:1287: in update_item
    item.update_with_attribute_updates(attribute_updates)
moto/dynamodb2/models/__init__.py:121: in update_with_attribute_updates
    self.attrs[attribute_name] = DynamoType({"L": new_value})
moto/dynamodb2/models/dynamo_type.py:64: in __init__
    self.value = [DynamoType(val) for val in self.value]
moto/dynamodb2/models/dynamo_type.py:64: in <listcomp>
    self.value = [DynamoType(val) for val in self.value]
```


Discussion:
Thanks for raising this @Naivehahaha - marking it as a bug.

Resolving patch:
diff --git a/moto/dynamodb2/models/__init__.py b/moto/dynamodb2/models/__init__.py
--- a/moto/dynamodb2/models/__init__.py
+++ b/moto/dynamodb2/models/__init__.py
@@ -128,10 +128,10 @@ def update_with_attribute_updates(self, attribute_updates):
             new_value = list(update_action["Value"].values())[0]
             if action == "PUT":
                 # TODO deal with other types
-                if isinstance(new_value, list):
-                    self.attrs[attribute_name] = DynamoType({"L": new_value})
-                elif isinstance(new_value, set):
+                if set(update_action["Value"].keys()) == set(["SS"]):
                     self.attrs[attribute_name] = DynamoType({"SS": new_value})
+                elif isinstance(new_value, list):
+                    self.attrs[attribute_name] = DynamoType({"L": new_value})
                 elif isinstance(new_value, dict):
                     self.attrs[attribute_name] = DynamoType({"M": new_value})
                 elif set(update_action["Value"].keys()) == set(["N"]):


Test patch:
diff --git a/tests/test_dynamodb2/test_dynamodb.py b/tests/test_dynamodb2/test_dynamodb.py
--- a/tests/test_dynamodb2/test_dynamodb.py
+++ b/tests/test_dynamodb2/test_dynamodb.py
@@ -5628,3 +5628,24 @@ def test_transact_get_items_should_return_empty_map_for_non_existent_item():
     items.should.have.length_of(2)
     items[0].should.equal({"Item": item})
     items[1].should.equal({})
+
+
+@mock_dynamodb2
+def test_dynamodb_update_item_fails_on_string_sets():
+    dynamodb = boto3.resource("dynamodb", region_name="eu-west-1")
+    client = boto3.client("dynamodb", region_name="eu-west-1")
+
+    table = dynamodb.create_table(
+        TableName="test",
+        KeySchema=[{"AttributeName": "record_id", "KeyType": "HASH"},],
+        AttributeDefinitions=[{"AttributeName": "record_id", "AttributeType": "S"},],
+        BillingMode="PAY_PER_REQUEST",
+    )
+    table.meta.client.get_waiter("table_exists").wait(TableName="test")
+    attribute = {"test_field": {"Value": {"SS": ["test1", "test2"],}, "Action": "PUT"}}
+
+    client.update_item(
+        TableName="test",
+        Key={"record_id": {"S": "testrecord"}},
+        AttributeUpdates=attribute,
+    )