Repository: getmoto/moto (python)

Issue:
No ValidationException raised when calling DDB UpdateItem operation with an empty ExpressionAttributeValues
If you pass a blank ExpressionAttributeValues parameter to the DDB update_item it should raise a ValidationException. With moto the code executes and the update is performed successfully. 

The following
```python
        updateExpression = "REMOVE #records.#key"
        expressionAttributeNames = {"#records": "Records", "#key": key}
        expressionAttributeValues = {}
        itemkeys = self.keygen({"Name": lookup_name}, "Lookup#")
        self.table.update_item(
            Key={"key": itemkeys["key"], "sort": itemkeys["sort"]},
            UpdateExpression=updateExpression,
            ExpressionAttributeNames=expressionAttributeNames,
            ExpressionAttributeValues=expressionAttributeValues,
        )
```

produces

```shell
  File "... /nc_ddb.py", line 127, in DDBupdate_item
    return self.table.update_item(**kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/rodos/.pyenv/versions/3.12.2/lib/python3.12/site-packages/boto3/resources/factory.py", line 581, in do_action
    response = action(self, *args, **kwargs)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/rodos/.pyenv/versions/3.12.2/lib/python3.12/site-packages/boto3/resources/action.py", line 88, in __call__
    response = getattr(parent.meta.client, operation_name)(*args, **params)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/rodos/.pyenv/versions/3.12.2/lib/python3.12/site-packages/botocore/client.py", line 565, in _api_call
    return self._make_api_call(operation_name, kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/rodos/.pyenv/versions/3.12.2/lib/python3.12/site-packages/botocore/client.py", line 1021, in _make_api_call
    raise error_class(parsed_response, operation_name)
botocore.exceptions.ClientError: An error occurred (ValidationException) when calling the UpdateItem operation: ExpressionAttributeValues must not be empty
```

This is when using mocks and with latest version 
```shell
source % pip freeze | grep -i 'moto\|boto3'
boto3==1.34.157
moto==5.0.12
```

Its a small thing but when doing TDD my tests did not pick this up and the error did not occur until I started using this against the DynamoDB local instance. I assume the same error is produced against the DDB service given the local instance does it.

Great library, Moto is really just fantastic!

Pull request:
DynamoDB: update_item() now validates an empty ExpressionAttributeVal…
…ues-parameter

Fixes #7952 

Patch:
diff --git a/moto/dynamodb/responses.py b/moto/dynamodb/responses.py
index d77ed7cdf..9d000ffbf 100644
--- a/moto/dynamodb/responses.py
+++ b/moto/dynamodb/responses.py
@@ -965,7 +965,11 @@ class DynamoHandler(BaseResponse):
         return dynamo_json_dump(item_dict)
 
     def _get_expr_attr_values(self) -> Dict[str, Dict[str, str]]:
-        values = self.body.get("ExpressionAttributeValues", {})
+        values = self.body.get("ExpressionAttributeValues")
+        if values is None:
+            return {}
+        if len(values) == 0:
+            raise MockValidationException("ExpressionAttributeValues must not be empty")
         for key in values.keys():
             if not key.startswith(":"):
                 raise MockValidationException(
diff --git a/moto/organizations/models.py b/moto/organizations/models.py
index eacdcdee6..2f0f03b18 100644
--- a/moto/organizations/models.py
+++ b/moto/organizations/models.py
@@ -88,6 +88,35 @@ class FakeAccount(BaseModel):
         self.attached_policies: List[FakePolicy] = []
         self.tags = {tag["Key"]: tag["Value"] for tag in kwargs.get("Tags", [])}
 
+        role_name = kwargs.get("RoleName", "OrganizationAccountAccessRole")
+
+        from moto.iam import iam_backends
+        from moto.iam.exceptions import EntityAlreadyExists
+
+        trust_policy = {
+            "Version": "2012-10-17",
+            "Statement": [
+                {
+                    "Effect": "Allow",
+                    "Principal": {"AWS": f"arn:aws:iam::{self.master_account_id}:root"},
+                    "Action": "sts:AssumeRole",
+                }
+            ],
+        }
+        iam = iam_backends[self.id]["global"]
+        try:
+            iam.create_role(
+                role_name=role_name,
+                assume_role_policy_document=json.dumps(trust_policy),
+                path="",
+                permissions_boundary=None,
+                description="",
+                tags=[],
+                max_session_duration="3600",
+            )
+        except EntityAlreadyExists:
+            pass
+
     @property
     def arn(self) -> str:
         partition = get_partition(self.region)


Test patch:
diff --git a/tests/test_dynamodb/test_dynamodb_update_expressions.py b/tests/test_dynamodb/test_dynamodb_update_expressions.py
index 5ffcedbac..526b00fce 100644
--- a/tests/test_dynamodb/test_dynamodb_update_expressions.py
+++ b/tests/test_dynamodb/test_dynamodb_update_expressions.py
@@ -187,3 +187,22 @@ def test_update_item_add_empty_set(table_name=None):
     assert dynamodb.scan(TableName=table_name)["Items"] == [
         {"pk": {"S": "foo"}, "stringset": {"SS": ["item1"]}}
     ]
+
+
+@pytest.mark.aws_verified
+@dynamodb_aws_verified()
+def test_update_item_with_empty_values(table_name=None):
+    dynamodb = boto3.client("dynamodb", "us-east-1")
+
+    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="SET #d = :s",
+            ExpressionAttributeNames={"#d": "d"},
+            ExpressionAttributeValues={},
+        )
+    err = exc.value.response["Error"]
+    assert err["Code"] == "ValidationException"
+    assert err["Message"] == "ExpressionAttributeValues must not be empty"
diff --git a/tests/test_organizations/test_organizations_boto3.py b/tests/test_organizations/test_organizations_boto3.py
index 9fa3d567d..5c1ee7d18 100644
--- a/tests/test_organizations/test_organizations_boto3.py
+++ b/tests/test_organizations/test_organizations_boto3.py
@@ -1,12 +1,14 @@
 import json
+import os
 import re
 from datetime import datetime
+from unittest import SkipTest, mock
 
 import boto3
 import pytest
 from botocore.exceptions import ClientError
 
-from moto import mock_aws
+from moto import mock_aws, settings
 from moto.core import DEFAULT_ACCOUNT_ID as ACCOUNT_ID
 from moto.organizations import utils
 from moto.organizations.exceptions import InvalidInputException, TargetNotFoundException
@@ -75,6 +77,39 @@ def test_create_organization_without_feature_set():
     assert response["Organization"]["FeatureSet"] == "ALL"
 
 
+@mock_aws
+def test_create_account_creates_role():
+    if not settings.TEST_DECORATOR_MODE:
+        raise SkipTest("Involves changing account using env variable")
+    orgs_client = boto3.client("organizations", region_name="us-west-2")
+    orgs_client.create_organization()
+    account = orgs_client.create_account(AccountName="test", Email="test@test.xyz")
+    account_id = account["CreateAccountStatus"]["AccountId"]
+
+    with mock.patch.dict(os.environ, {"MOTO_ACCOUNT_ID": account_id}):
+        iam_client = boto3.client("iam", "us-east-1")
+        iam_client.get_role(RoleName="OrganizationAccountAccessRole")
+
+
+@mock_aws
+def test_create_account_creates_custom_role():
+    if not settings.TEST_DECORATOR_MODE:
+        raise SkipTest("Involves changing account using env variable")
+
+    orgs_client = boto3.client("organizations", region_name="us-west-2")
+    _ = orgs_client.create_organization()
+    account = orgs_client.create_account(
+        AccountName="test_account",
+        Email="test@test.xyz",
+        RoleName="CustomOrganizationRole",
+    )
+    account_id = account["CreateAccountStatus"]["AccountId"]
+
+    with mock.patch.dict(os.environ, {"MOTO_ACCOUNT_ID": account_id}):
+        iam_client = boto3.client("iam", "us-east-1")
+        iam_client.get_role(RoleName="CustomOrganizationRole")
+
+
 @mock_aws
 def test_describe_organization():
     client = boto3.client("organizations", region_name="us-east-1")