|
1 | 1 | import datetime |
2 | 2 | import uuid |
3 | 3 | from decimal import Decimal |
| 4 | +from operator import attrgetter |
4 | 5 |
|
5 | 6 | from bson import ObjectId |
| 7 | +from django.db import DatabaseError |
| 8 | +from django.db.models import Avg |
6 | 9 |
|
7 | 10 | from django_mongodb_backend.fields import ( |
8 | 11 | EncryptedArrayField, |
|
18 | 21 | BigIntegerModel, |
19 | 22 | Billing, |
20 | 23 | BinaryModel, |
| 24 | + Book, |
21 | 25 | BooleanModel, |
22 | 26 | CharModel, |
23 | 27 | DateModel, |
@@ -214,6 +218,146 @@ def test_time(self): |
214 | 218 | self.assertEncrypted(TimeModel, "value") |
215 | 219 |
|
216 | 220 |
|
| 221 | +class QueryTests(EncryptionTestCase): |
| 222 | + def test_aggregate(self): |
| 223 | + msg = ( |
| 224 | + "Aggregation stage $internalFacetTeeConsumer is not allowed or " |
| 225 | + "supported with automatic encryption." |
| 226 | + ) |
| 227 | + with self.assertRaisesMessage(DatabaseError, msg): |
| 228 | + list(IntegerModel.objects.aggregate(Avg("value"))) |
| 229 | + |
| 230 | + def test_alias(self): |
| 231 | + msg = ( |
| 232 | + "Cannot group on field '_id.value' which is encrypted with the " |
| 233 | + "random algorithm or whose encryption properties are not known " |
| 234 | + "until runtime" |
| 235 | + ) |
| 236 | + with self.assertRaisesMessage(DatabaseError, msg): |
| 237 | + list(IntegerModel.objects.alias(avg=Avg("value"))) |
| 238 | + |
| 239 | + def test_annotate(self): |
| 240 | + msg = ( |
| 241 | + "Cannot group on field '_id.value' which is encrypted with the " |
| 242 | + "random algorithm or whose encryption properties are not known " |
| 243 | + "until runtime" |
| 244 | + ) |
| 245 | + with self.assertRaisesMessage(DatabaseError, msg): |
| 246 | + list(IntegerModel.objects.annotate(avg=Avg("value"))) |
| 247 | + |
| 248 | + def test_bulk_create(self): |
| 249 | + CharModel.objects.bulk_create([CharModel(value="abc"), CharModel(value="xyz")]) |
| 250 | + self.assertQuerySetEqual( |
| 251 | + CharModel.objects.order_by("pk"), ["abc", "xyz"], attrgetter("value") |
| 252 | + ) |
| 253 | + |
| 254 | + def test_bulk_update(self): |
| 255 | + objs = [ |
| 256 | + CharModel.objects.create(value="abc"), |
| 257 | + CharModel.objects.create(value="xyz"), |
| 258 | + ] |
| 259 | + objs[0].value = "def" |
| 260 | + objs[1].value = "mno" |
| 261 | + msg = "Multi-document updates are not allowed with Queryable Encryption" |
| 262 | + with self.assertRaisesMessage(DatabaseError, msg): |
| 263 | + CharModel.objects.bulk_update(objs, ["value"]) |
| 264 | + |
| 265 | + def test_contains(self): |
| 266 | + obj = CharModel.objects.create(value="abc") |
| 267 | + self.assertIs(CharModel.objects.contains(obj), True) |
| 268 | + |
| 269 | + def test_count(self): |
| 270 | + msg = ( |
| 271 | + "Aggregation stage $internalFacetTeeConsumer is not allowed or " |
| 272 | + "supported with automatic encryption." |
| 273 | + ) |
| 274 | + with self.assertRaisesMessage(DatabaseError, msg): |
| 275 | + list(CharModel.objects.count()) |
| 276 | + |
| 277 | + def test_dates(self): |
| 278 | + msg = ( |
| 279 | + "If the value type is a date, the type of the index must also be date (and vice versa)." |
| 280 | + ) |
| 281 | + with self.assertRaisesMessage(DatabaseError, msg): |
| 282 | + list(DateModel.objects.dates("value", "year")) |
| 283 | + |
| 284 | + def test_datetimes(self): |
| 285 | + msg = ( |
| 286 | + "If the value type is a date, the type of the index must also be date (and vice versa)." |
| 287 | + ) |
| 288 | + with self.assertRaisesMessage(DatabaseError, msg): |
| 289 | + list(DateTimeModel.objects.datetimes("value", "year")) |
| 290 | + |
| 291 | + def test_distinct(self): |
| 292 | + msg = ( |
| 293 | + "Cannot group on field '_id.value' which is encrypted with the " |
| 294 | + "random algorithm or whose encryption properties are not known " |
| 295 | + "until runtime" |
| 296 | + ) |
| 297 | + with self.assertRaisesMessage(DatabaseError, msg): |
| 298 | + list(CharModel.objects.distinct("value")) |
| 299 | + |
| 300 | + def test_exclude(self): |
| 301 | + obj1 = CharModel.objects.create(value="abc") |
| 302 | + obj2 = CharModel.objects.create(value="xyz") |
| 303 | + self.assertSequenceEqual(CharModel.objects.exclude(value=obj1.value), [obj2]) |
| 304 | + |
| 305 | + def test_exists(self): |
| 306 | + self.assertIs(CharModel.objects.exists(), False) |
| 307 | + |
| 308 | + def test_get_or_create(self): |
| 309 | + obj1, created1 = CharModel.objects.get_or_create(value="abc") |
| 310 | + self.assertIs(created1, True) |
| 311 | + obj2, created2 = CharModel.objects.get_or_create(value="abc") |
| 312 | + self.assertIs(created2, False) |
| 313 | + self.assertEqual(obj1, obj2) |
| 314 | + |
| 315 | + def test_join(self): |
| 316 | + msg = ( |
| 317 | + "Non-empty 'let' field is not allowed in the $lookup aggregation " |
| 318 | + "stage over an encrypted collection." |
| 319 | + ) |
| 320 | + with self.assertRaisesMessage(DatabaseError, msg): |
| 321 | + list(Book.objects.filter(author__name="xxx")) |
| 322 | + |
| 323 | + def test_order_by(self): |
| 324 | + msg = "Cannot add an encrypted field as a prefix of another encrypted field" |
| 325 | + with self.assertRaisesMessage(DatabaseError, msg): |
| 326 | + list(CharModel.objects.order_by("value")) |
| 327 | + |
| 328 | + def test_select_related(self): |
| 329 | + msg = ( |
| 330 | + "Non-empty 'let' field is not allowed in the $lookup aggregation " |
| 331 | + "stage over an encrypted collection." |
| 332 | + ) |
| 333 | + with self.assertRaisesMessage(DatabaseError, msg): |
| 334 | + list(Book.objects.select_related("author")) |
| 335 | + |
| 336 | + def test_update(self): |
| 337 | + msg = "Multi-document updates are not allowed with Queryable Encryption" |
| 338 | + with self.assertRaisesMessage(DatabaseError, msg): |
| 339 | + self.assertEqual(CharModel.objects.update(value="xyz"), 1) |
| 340 | + |
| 341 | + def test_update_or_create(self): |
| 342 | + CharModel.objects.create(value="xyz") |
| 343 | + msg = "Multi-document updates are not allowed with Queryable Encryption" |
| 344 | + with self.assertRaisesMessage(DatabaseError, msg): |
| 345 | + CharModel.objects.update_or_create(value="xyz", defaults={"plain": "abc"}) |
| 346 | + |
| 347 | + def test_union(self): |
| 348 | + msg = "Aggregation stage $unionWith is not allowed or supported with automatic encryption." |
| 349 | + qs1 = IntegerModel.objects.filter(value__gt=1) |
| 350 | + qs2 = IntegerModel.objects.filter(value__gte=8) |
| 351 | + with self.assertRaisesMessage(DatabaseError, msg): |
| 352 | + list(qs1.union(qs2)) |
| 353 | + |
| 354 | + def test_values(self): |
| 355 | + list(CharModel.objects.values("value")) |
| 356 | + |
| 357 | + def test_values_list(self): |
| 358 | + list(CharModel.objects.values_list("value")) |
| 359 | + |
| 360 | + |
217 | 361 | class FieldMixinTests(EncryptionTestCase): |
218 | 362 | def test_db_index(self): |
219 | 363 | msg = "'db_index=True' is not supported on encrypted fields." |
|
0 commit comments