Repository: getmoto/moto

Issue:
DynamoDB: Update item doesn't validate against attributes
Might be related to https://github.com/spulec/moto/issues/2396

Moto doesn't return an error when using putting the wrong attribute type.

Example:
When updating an attribute defined as:
```
table = dynamodb.create_table(
    ...
    AttributeDefinitions = [{
        'AttributeName': 'this_is_a_number',
        'AttributeType': 'N'
    }]
)
```
with "put item", using a string value such as "123123jkasdfsdf", moto doesn't return a 400 with a client error.

Expected:
"400 status" and a "The parameter cannot be converted to a numeric value" message (or similar)

Actual: 
2xx status

Version: 1.3.15.dev1023
Using through pynamodb
Installed as a mock, boto versions:
boto3==1.14.30  
boto==2.48.0   
botocore==1.17.30 


Discussion:
Thanks for raising this @meshuamam - marking this as an enhancement

Resolving patch:
diff --git a/moto/dynamodb2/exceptions.py b/moto/dynamodb2/exceptions.py
--- a/moto/dynamodb2/exceptions.py
+++ b/moto/dynamodb2/exceptions.py
@@ -206,6 +206,13 @@ def __init__(self, key_name):
         super(UpdateHashRangeKeyException, self).__init__(self.msg.format(key_name))
 
 
+class InvalidAttributeTypeError(MockValidationException):
+    msg = "One or more parameter values were invalid: Type mismatch for key {} expected: {} actual: {}"
+
+    def __init__(self, name, expected_type, actual_type):
+        super().__init__(self.msg.format(name, expected_type, actual_type))
+
+
 class TooManyAddClauses(InvalidUpdateExpression):
     msg = 'The "ADD" section can only be used once in an update expression;'
 
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
@@ -23,6 +23,7 @@
     ConditionalCheckFailed,
     TransactionCanceledException,
     EmptyKeyAttributeException,
+    InvalidAttributeTypeError,
 )
 from moto.dynamodb2.models.utilities import bytesize
 from moto.dynamodb2.models.dynamo_type import DynamoType
@@ -67,11 +68,9 @@ def __setitem__(self, key, value):
 
 
 class Item(BaseModel):
-    def __init__(self, hash_key, hash_key_type, range_key, range_key_type, attrs):
+    def __init__(self, hash_key, range_key, attrs):
         self.hash_key = hash_key
-        self.hash_key_type = hash_key_type
         self.range_key = range_key
-        self.range_key_type = range_key_type
 
         if hash_key and hash_key.size() > HASH_KEY_MAX_LENGTH:
             raise HashKeyTooLong
@@ -86,9 +85,7 @@ def __eq__(self, other):
         return all(
             [
                 self.hash_key == other.hash_key,
-                self.hash_key_type == other.hash_key_type,
                 self.range_key == other.range_key,
-                self.range_key_type == other.range_key_type,
                 self.attrs == other.attrs,
             ]
         )
@@ -411,12 +408,17 @@ def __init__(
         self.range_key_type = None
         self.hash_key_type = None
         for elem in schema:
+            attr_type = [
+                a["AttributeType"]
+                for a in attr
+                if a["AttributeName"] == elem["AttributeName"]
+            ][0]
             if elem["KeyType"] == "HASH":
                 self.hash_key_attr = elem["AttributeName"]
-                self.hash_key_type = elem["KeyType"]
+                self.hash_key_type = attr_type
             else:
                 self.range_key_attr = elem["AttributeName"]
-                self.range_key_type = elem["KeyType"]
+                self.range_key_type = attr_type
         self.table_key_attrs = [
             key for key in (self.hash_key_attr, self.range_key_attr) if key
         ]
@@ -620,6 +622,18 @@ def put_item(
         else:
             range_value = None
 
+        if hash_value.type != self.hash_key_type:
+            raise InvalidAttributeTypeError(
+                self.hash_key_attr,
+                expected_type=self.hash_key_type,
+                actual_type=hash_value.type,
+            )
+        if range_value and range_value.type != self.range_key_type:
+            raise InvalidAttributeTypeError(
+                self.range_key_attr,
+                expected_type=self.range_key_type,
+                actual_type=range_value.type,
+            )
         if expected is None:
             expected = {}
             lookup_range_value = range_value
@@ -630,9 +644,7 @@ def put_item(
             else:
                 lookup_range_value = DynamoType(expected_range_value)
         current = self.get_item(hash_value, lookup_range_value)
-        item = Item(
-            hash_value, self.hash_key_type, range_value, self.range_key_type, item_attrs
-        )
+        item = Item(hash_value, range_value, item_attrs)
 
         if not overwrite:
             if not get_expected(expected).expr(current):
@@ -1427,13 +1439,7 @@ def update_item(
         if item is None:
             if update_expression:
                 # Validate AST before creating anything
-                item = Item(
-                    hash_value,
-                    table.hash_key_type,
-                    range_value,
-                    table.range_key_type,
-                    attrs={},
-                )
+                item = Item(hash_value, range_value, attrs={},)
                 UpdateExpressionValidator(
                     update_expression_ast,
                     expression_attribute_names=expression_attribute_names,


Test patch:
diff --git a/tests/test_dynamodb2/conftest.py b/tests/test_dynamodb2/conftest.py
--- a/tests/test_dynamodb2/conftest.py
+++ b/tests/test_dynamodb2/conftest.py
@@ -10,4 +10,8 @@ def table():
             {"KeyType": "HASH", "AttributeName": "forum_name"},
             {"KeyType": "RANGE", "AttributeName": "subject"},
         ],
+        attr=[
+            {"AttributeType": "S", "AttributeName": "forum_name"},
+            {"AttributeType": "S", "AttributeName": "subject"},
+        ],
     )
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
@@ -35,6 +35,10 @@ def test_list_tables():
     # Should make tables properly with boto
     dynamodb_backend2.create_table(
         name,
+        attr=[
+            {"AttributeType": "S", "AttributeName": "forum_name"},
+            {"AttributeType": "S", "AttributeName": "subject"},
+        ],
         schema=[
             {"KeyType": "HASH", "AttributeName": "forum_name"},
             {"KeyType": "RANGE", "AttributeName": "subject"},
@@ -70,10 +74,14 @@ def test_list_tables_boto3(names):
 def test_list_tables_layer_1():
     # Should make tables properly with boto
     dynamodb_backend2.create_table(
-        "test_1", schema=[{"KeyType": "HASH", "AttributeName": "name"}]
+        "test_1",
+        attr=[{"AttributeType": "S", "AttributeName": "name"},],
+        schema=[{"KeyType": "HASH", "AttributeName": "name"}],
     )
     dynamodb_backend2.create_table(
-        "test_2", schema=[{"KeyType": "HASH", "AttributeName": "name"}]
+        "test_2",
+        attr=[{"AttributeType": "S", "AttributeName": "name"}],
+        schema=[{"KeyType": "HASH", "AttributeName": "name"}],
     )
     conn = boto.dynamodb2.connect_to_region(
         "us-east-1", aws_access_key_id="ak", aws_secret_access_key="sk"
@@ -1485,11 +1493,9 @@ def test_put_item_nonexisting_range_key():
 
 def test_filter_expression():
     row1 = moto.dynamodb2.models.Item(
-        None,
-        None,
-        None,
-        None,
-        {
+        hash_key=None,
+        range_key=None,
+        attrs={
             "Id": {"N": "8"},
             "Subs": {"N": "5"},
             "Desc": {"S": "Some description"},
@@ -1497,11 +1503,9 @@ def test_filter_expression():
         },
     )
     row2 = moto.dynamodb2.models.Item(
-        None,
-        None,
-        None,
-        None,
-        {
+        hash_key=None,
+        range_key=None,
+        attrs={
             "Id": {"N": "8"},
             "Subs": {"N": "10"},
             "Desc": {"S": "A description"},
diff --git a/tests/test_dynamodb2/test_dynamodb_exceptions.py b/tests/test_dynamodb2/test_dynamodb_exceptions.py
--- a/tests/test_dynamodb2/test_dynamodb_exceptions.py
+++ b/tests/test_dynamodb2/test_dynamodb_exceptions.py
@@ -328,3 +328,49 @@ def test_create_table_with_redundant_and_missing_attributes():
     err["Message"].should.equal(
         "One or more parameter values were invalid: Some index key attributes are not defined in AttributeDefinitions. Keys: [user], AttributeDefinitions: [created_at, id]"
     )
+
+
+@mock_dynamodb2
+def test_put_item_wrong_attribute_type():
+    dynamodb = boto3.client("dynamodb", region_name="us-east-1")
+
+    dynamodb.create_table(
+        TableName="test-table",
+        AttributeDefinitions=[
+            {"AttributeName": "id", "AttributeType": "S"},
+            {"AttributeName": "created_at", "AttributeType": "N"},
+        ],
+        KeySchema=[
+            {"AttributeName": "id", "KeyType": "HASH"},
+            {"AttributeName": "created_at", "KeyType": "RANGE"},
+        ],
+        BillingMode="PAY_PER_REQUEST",
+    )
+
+    item = {
+        "id": {"N": "1"},  # should be a string
+        "created_at": {"N": "2"},
+        "someAttribute": {"S": "lore ipsum"},
+    }
+
+    with pytest.raises(ClientError) as exc:
+        dynamodb.put_item(TableName="test-table", Item=item)
+    err = exc.value.response["Error"]
+    err["Code"].should.equal("ValidationException")
+    err["Message"].should.equal(
+        "One or more parameter values were invalid: Type mismatch for key id expected: S actual: N"
+    )
+
+    item = {
+        "id": {"S": "some id"},
+        "created_at": {"S": "should be date not string"},
+        "someAttribute": {"S": "lore ipsum"},
+    }
+
+    with pytest.raises(ClientError) as exc:
+        dynamodb.put_item(TableName="test-table", Item=item)
+    err = exc.value.response["Error"]
+    err["Code"].should.equal("ValidationException")
+    err["Message"].should.equal(
+        "One or more parameter values were invalid: Type mismatch for key created_at expected: N actual: S"
+    )
diff --git a/tests/test_dynamodb2/test_dynamodb_executor.py b/tests/test_dynamodb2/test_dynamodb_executor.py
--- a/tests/test_dynamodb2/test_dynamodb_executor.py
+++ b/tests/test_dynamodb2/test_dynamodb_executor.py
@@ -12,9 +12,7 @@ def test_execution_of_if_not_exists_not_existing_value(table):
     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(
@@ -27,9 +25,7 @@ def test_execution_of_if_not_exists_not_existing_value(table):
     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": "A"}},
     )
     assert expected_item == item
@@ -42,9 +38,7 @@ def test_execution_of_if_not_exists_with_existing_attribute_should_return_attrib
     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(
@@ -57,9 +51,7 @@ def test_execution_of_if_not_exists_with_existing_attribute_should_return_attrib
     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
@@ -71,9 +63,7 @@ def test_execution_of_if_not_exists_with_existing_attribute_should_return_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": "1"}, "b": {"N": "3"}},
     )
     validated_ast = UpdateExpressionValidator(
@@ -86,9 +76,7 @@ def test_execution_of_if_not_exists_with_existing_attribute_should_return_value(
     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"}, "b": {"N": "3"}, "a": {"N": "3"}},
     )
     assert expected_item == item
@@ -101,11 +89,7 @@ def test_execution_of_if_not_exists_with_non_existing_attribute_should_return_va
     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"}},
+        hash_key=DynamoType({"S": "id"}), range_key=None, attrs={"id": {"S": "1"}},
     )
     validated_ast = UpdateExpressionValidator(
         update_expression_ast,
@@ -117,9 +101,7 @@ def test_execution_of_if_not_exists_with_non_existing_attribute_should_return_va
     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": "4"}},
     )
     assert expected_item == item
@@ -130,9 +112,7 @@ def test_execution_of_sum_operation(table):
     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(
@@ -145,9 +125,7 @@ def test_execution_of_sum_operation(table):
     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
@@ -158,9 +136,7 @@ def test_execution_of_remove(table):
     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(
@@ -173,9 +149,7 @@ def test_execution_of_remove(table):
     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"}, "b": {"N": "4"}},
     )
     assert expected_item == item
@@ -186,9 +160,7 @@ def test_execution_of_remove_in_map(table):
     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": {
@@ -213,9 +185,7 @@ def test_execution_of_remove_in_map(table):
     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": {
@@ -238,9 +208,7 @@ def test_execution_of_remove_in_list(table):
     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": {
@@ -265,9 +233,7 @@ def test_execution_of_remove_in_list(table):
     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": {
@@ -289,9 +255,7 @@ def test_execution_of_delete_element_from_set(table, attr_name):
     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(
@@ -304,9 +268,7 @@ def test_execution_of_delete_element_from_set(table, attr_name):
     UpdateExpressionExecutor(validated_ast, item, expression_attribute_names).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", "value3"]},},
     )
     assert expected_item == item
@@ -323,11 +285,7 @@ def test_execution_of_delete_element_from_set(table, attr_name):
     ).validate()
     UpdateExpressionExecutor(validated_ast, item, expression_attribute_names).execute()
     expected_item = Item(
-        hash_key=DynamoType({"S": "id"}),
-        hash_key_type="TYPE",
-        range_key=None,
-        range_key_type=None,
-        attrs={"id": {"S": "foo2"}},
+        hash_key=DynamoType({"S": "id"}), range_key=None, attrs={"id": {"S": "foo2"}},
     )
     assert expected_item == item
 
@@ -337,9 +295,7 @@ def test_execution_of_add_number(table):
     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(
@@ -352,9 +308,7 @@ def test_execution_of_add_number(table):
     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
@@ -365,9 +319,7 @@ def test_execution_of_add_set_to_a_number(table):
     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:
@@ -381,9 +333,7 @@ def test_execution_of_add_set_to_a_number(table):
         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
@@ -397,9 +347,7 @@ def test_execution_of_add_to_a_set(table):
     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(
@@ -412,9 +360,7 @@ def test_execution_of_add_to_a_set(table):
     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"]},
@@ -443,9 +389,7 @@ def test_execution_of__delete_element_from_set_invalid_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:
@@ -469,9 +413,7 @@ def test_execution_of_delete_element_from_a_string_attribute(table):
     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:
diff --git a/tests/test_dynamodb2/test_dynamodb_table_with_range_key.py b/tests/test_dynamodb2/test_dynamodb_table_with_range_key.py
--- a/tests/test_dynamodb2/test_dynamodb_table_with_range_key.py
+++ b/tests/test_dynamodb2/test_dynamodb_table_with_range_key.py
@@ -400,8 +400,8 @@ def test_get_item_without_range_key():
         throughput={"read": 10, "write": 10},
     )
 
-    hash_key = 3241526475
-    range_key = 1234567890987
+    hash_key = "3241526475"
+    range_key = "1234567890987"
     table.put_item(data={"test_hash": hash_key, "test_range": range_key})
     table.get_item.when.called_with(test_hash=hash_key).should.throw(
         ValidationException
@@ -424,8 +424,8 @@ def test_get_item_without_range_key_boto3():
         ProvisionedThroughput={"ReadCapacityUnits": 1, "WriteCapacityUnits": 5},
     )
 
-    hash_key = 3241526475
-    range_key = 1234567890987
+    hash_key = "3241526475"
+    range_key = "1234567890987"
     table.put_item(Item={"id": hash_key, "subject": range_key})
 
     with pytest.raises(ClientError) as ex:
@@ -1077,7 +1077,10 @@ def test_reverse_query():
 def test_lookup():
     table = Table.create(
         "messages",
-        schema=[HashKey("test_hash"), RangeKey("test_range")],
+        schema=[
+            HashKey("test_hash", data_type=NUMBER),
+            RangeKey("test_range", data_type=NUMBER),
+        ],
         throughput={"read": 10, "write": 10},
     )
 
diff --git a/tests/test_dynamodb2/test_dynamodb_table_without_range_key.py b/tests/test_dynamodb2/test_dynamodb_table_without_range_key.py
--- a/tests/test_dynamodb2/test_dynamodb_table_without_range_key.py
+++ b/tests/test_dynamodb2/test_dynamodb_table_without_range_key.py
@@ -12,6 +12,7 @@
 import botocore
 
 try:
+    from boto.dynamodb2.types import NUMBER
     from boto.dynamodb2.fields import HashKey
     from boto.dynamodb2.table import Table
     from boto.dynamodb2.table import Item
@@ -660,7 +661,7 @@ def test_get_missing_item():
 def test_get_special_item():
     table = Table.create(
         "messages",
-        schema=[HashKey("date-joined")],
+        schema=[HashKey("date-joined", data_type=NUMBER)],
         throughput={"read": 10, "write": 10},
     )
 
diff --git a/tests/test_dynamodb2/test_dynamodb_validation.py b/tests/test_dynamodb2/test_dynamodb_validation.py
--- a/tests/test_dynamodb2/test_dynamodb_validation.py
+++ b/tests/test_dynamodb2/test_dynamodb_validation.py
@@ -27,9 +27,7 @@ def test_valid_update_expression(table):
     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(
@@ -48,9 +46,7 @@ def test_validation_of_update_expression_with_keyword(table):
         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(
@@ -80,9 +76,7 @@ def test_validation_of_a_set_statement_with_incorrect_passed_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": "1"}, "b": {"N": "3"}},
     )
     try:
@@ -111,9 +105,7 @@ def test_validation_of_update_expression_with_attribute_that_does_not_exist_in_i
         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(
@@ -144,9 +136,7 @@ def test_validation_of_update_expression_with_attribute_name_that_is_not_defined
         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(
@@ -167,9 +157,7 @@ def test_validation_of_if_not_exists_not_existing_invalid_replace_value(table):
         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(
@@ -211,9 +199,7 @@ def test_validation_of_if_not_exists_not_existing_value(table):
     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(
@@ -234,9 +220,7 @@ def test_validation_of_if_not_exists_with_existing_attribute_should_return_attri
     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(
@@ -256,9 +240,7 @@ def test_validation_of_if_not_exists_with_existing_attribute_should_return_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": "1"}, "b": {"N": "3"}},
     )
     validated_ast = UpdateExpressionValidator(
@@ -279,11 +261,7 @@ def test_validation_of_if_not_exists_with_non_existing_attribute_should_return_v
     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"}},
+        hash_key=DynamoType({"S": "id"}), range_key=None, attrs={"id": {"S": "1"}},
     )
     validated_ast = UpdateExpressionValidator(
         update_expression_ast,
@@ -301,9 +279,7 @@ def test_validation_of_sum_operation(table):
     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(
@@ -322,9 +298,7 @@ def test_validation_homogeneous_list_append_function(table):
     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(
@@ -345,9 +319,7 @@ def test_validation_hetereogenous_list_append_function(table):
     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(
@@ -374,9 +346,7 @@ def test_validation_list_append_function_with_non_list_arg(table):
         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(
@@ -403,9 +373,7 @@ def test_sum_with_incompatible_types(table):
         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(
@@ -425,9 +393,7 @@ def test_validation_of_subraction_operation(table):
     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(
@@ -451,9 +417,7 @@ def test_cannot_index_into_a_string(table):
         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(
@@ -476,9 +440,7 @@ def test_validation_set_path_does_not_need_to_be_resolvable_when_setting_a_new_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(
@@ -500,9 +462,7 @@ def test_validation_set_path_does_not_need_to_be_resolvable_but_must_be_creatabl
         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(