|
| 1 | +import copy |
1 | 2 | from decimal import Decimal |
2 | 3 | from uuid import uuid4 |
3 | 4 |
|
4 | 5 | import boto3 |
5 | 6 | import pytest |
6 | 7 | from boto3.dynamodb.conditions import Attr, Key |
7 | 8 |
|
8 | | -from moto import mock_aws |
| 9 | +from moto import mock_aws, settings |
| 10 | +from moto.core import DEFAULT_ACCOUNT_ID as ACCOUNT_ID |
| 11 | +from moto.dynamodb import dynamodb_backends |
| 12 | +from moto.dynamodb.models import DynamoType |
9 | 13 |
|
10 | 14 | from . import dynamodb_aws_verified |
11 | 15 |
|
@@ -153,6 +157,91 @@ def test_key_condition_expressions(): |
153 | 157 | assert results["Count"] == 1 |
154 | 158 |
|
155 | 159 |
|
| 160 | +@mock_aws |
| 161 | +@pytest.mark.requires_clean_slate |
| 162 | +def test_query_returns_detached_items(): |
| 163 | + if settings.TEST_SERVER_MODE: |
| 164 | + pytest.skip("Can't access backend directly in server mode") |
| 165 | + dynamodb = boto3.resource("dynamodb", region_name="us-east-1") |
| 166 | + table_name = f"T{uuid4()}" |
| 167 | + table = dynamodb.create_table( |
| 168 | + TableName=table_name, |
| 169 | + KeySchema=[ |
| 170 | + {"AttributeName": "pk", "KeyType": "HASH"}, |
| 171 | + {"AttributeName": "sk", "KeyType": "RANGE"}, |
| 172 | + ], |
| 173 | + AttributeDefinitions=[ |
| 174 | + {"AttributeName": "pk", "AttributeType": "S"}, |
| 175 | + {"AttributeName": "sk", "AttributeType": "S"}, |
| 176 | + ], |
| 177 | + ProvisionedThroughput={"ReadCapacityUnits": 5, "WriteCapacityUnits": 5}, |
| 178 | + ) |
| 179 | + |
| 180 | + table.put_item( |
| 181 | + Item={ |
| 182 | + "pk": "hash", |
| 183 | + "sk": "1", |
| 184 | + "payload": {"nested": "original"}, |
| 185 | + "tags": ["a", "b"], |
| 186 | + } |
| 187 | + ) |
| 188 | + |
| 189 | + # Use backend API to verify Item isolation (boto3 returns plain dicts). |
| 190 | + backend = dynamodb_backends[ACCOUNT_ID]["us-east-1"] |
| 191 | + items, _, _ = backend.query( |
| 192 | + table_name, |
| 193 | + {"S": "hash"}, |
| 194 | + None, |
| 195 | + [], |
| 196 | + limit=10, |
| 197 | + exclusive_start_key=None, |
| 198 | + scan_index_forward=True, |
| 199 | + projection_expressions=None, |
| 200 | + index_name=None, |
| 201 | + consistent_read=False, |
| 202 | + expr_names=None, |
| 203 | + expr_values=None, |
| 204 | + filter_expression=None, |
| 205 | + ) |
| 206 | + |
| 207 | + item = items[0] |
| 208 | + item.attrs["payload"].value["nested"].value = "changed" |
| 209 | + item.attrs["tags"].value[0].value = "changed" |
| 210 | + |
| 211 | + stored_item = backend.get_item(table_name, {"pk": {"S": "hash"}, "sk": {"S": "1"}}) |
| 212 | + assert stored_item is not None |
| 213 | + assert stored_item is not item |
| 214 | + assert stored_item.attrs["payload"].value["nested"].value == "original" |
| 215 | + assert stored_item.attrs["tags"].value[0].value == "a" |
| 216 | + |
| 217 | + |
| 218 | +@pytest.mark.parametrize( |
| 219 | + "attr_value", |
| 220 | + [ |
| 221 | + pytest.param({"S": "hello"}, id="string"), |
| 222 | + pytest.param({"N": "123"}, id="number"), |
| 223 | + pytest.param({"B": "aGVsbG8="}, id="binary"), |
| 224 | + pytest.param({"BOOL": True}, id="boolean"), |
| 225 | + pytest.param({"NULL": True}, id="null"), |
| 226 | + pytest.param({"SS": ["a", "b", "c"]}, id="string_set"), |
| 227 | + pytest.param({"NS": ["1", "2", "3"]}, id="number_set"), |
| 228 | + pytest.param({"BS": ["aGVsbG8=", "d29ybGQ="]}, id="binary_set"), |
| 229 | + pytest.param({"L": [{"S": "a"}, {"N": "1"}]}, id="list"), |
| 230 | + pytest.param({"M": {"key": {"S": "value"}}}, id="map"), |
| 231 | + ], |
| 232 | +) |
| 233 | +def test_dynamotype_deepcopy_all_types(attr_value): |
| 234 | + original = DynamoType(attr_value) |
| 235 | + copied = copy.deepcopy(original) |
| 236 | + |
| 237 | + assert copied == original |
| 238 | + assert copied is not original |
| 239 | + assert copied.type == original.type |
| 240 | + # For mutable containers, verify the value is a different object |
| 241 | + if original.is_list() or original.is_map() or original.is_set(): |
| 242 | + assert copied.value is not original.value |
| 243 | + |
| 244 | + |
156 | 245 | @pytest.mark.aws_verified |
157 | 246 | @dynamodb_aws_verified(add_range=True) |
158 | 247 | def test_query_pagination(table_name=None): |
|
0 commit comments