33
44import pytest
55from boto3 .dynamodb .conditions import Key
6+ from boto3 .dynamodb .types import Binary
67from django .core .exceptions import ObjectDoesNotExist
78from flag_engine .segments .constants import IN
89from mypy_boto3_dynamodb .service_resource import Table
10+ from pytest_django .fixtures import SettingsWrapper
911from pytest_mock import MockerFixture
1012from rest_framework .exceptions import NotFound
1113
2729from segments .models import Condition , Segment , SegmentRule
2830from util .engine_models .identities .models import IdentityModel
2931from util .mappers import (
30- map_environment_to_environment_document ,
32+ map_environment_to_compressed_environment_document ,
3133 map_identity_to_identity_document ,
3234)
3335
@@ -313,12 +315,6 @@ def test_get_segment_ids_returns_correct_segment_ids( # type: ignore[no-untyped
313315
314316 identity_document = map_identity_to_identity_document (identity )
315317 identity_uuid = identity_document ["identity_uuid" ]
316- environment_document = map_environment_to_environment_document (environment )
317-
318- mocked_environment_wrapper = mocker .patch (
319- "environments.dynamodb.wrappers.identity_wrapper.DynamoEnvironmentWrapper"
320- )
321- mocked_environment_wrapper .return_value .get_item .return_value = environment_document
322318
323319 dynamo_identity_wrapper = DynamoIdentityWrapper ()
324320 mocked_get_item_from_uuid = mocker .patch .object (
@@ -331,9 +327,6 @@ def test_get_segment_ids_returns_correct_segment_ids( # type: ignore[no-untyped
331327 # Then
332328 assert segment_ids == [identity_matching_segment .id ]
333329 mocked_get_item_from_uuid .assert_called_with (identity_uuid )
334- mocked_environment_wrapper .return_value .get_item .assert_called_with (
335- environment .api_key
336- )
337330
338331
339332def test_get_segment_ids_with_segment_feature_overrides (
@@ -388,12 +381,6 @@ def test_get_segment_ids_with_segment_feature_overrides(
388381
389382 identity_document = map_identity_to_identity_document (identity )
390383 identity_uuid = identity_document ["identity_uuid" ]
391- environment_document = map_environment_to_environment_document (environment )
392-
393- mocked_environment_wrapper = mocker .patch (
394- "environments.dynamodb.wrappers.identity_wrapper.DynamoEnvironmentWrapper"
395- )
396- mocked_environment_wrapper .return_value .get_item .return_value = environment_document
397384
398385 dynamo_identity_wrapper = DynamoIdentityWrapper ()
399386 mocker .patch .object (
@@ -430,12 +417,6 @@ def test_get_segment_ids_returns_segment_using_in_operator_for_integer_traits(
430417
431418 identity_document = map_identity_to_identity_document (identity )
432419 identity_uuid = identity_document ["identity_uuid" ]
433- environment_document = map_environment_to_environment_document (environment )
434-
435- mocked_environment_wrapper = mocker .patch (
436- "environments.dynamodb.wrappers.identity_wrapper.DynamoEnvironmentWrapper"
437- )
438- mocked_environment_wrapper .return_value .get_item .return_value = environment_document
439420
440421 dynamo_identity_wrapper = DynamoIdentityWrapper ()
441422 mocker .patch .object (
@@ -495,12 +476,6 @@ def test_get_segment_ids_with_identity_model(identity, environment, mocker): #
495476 # Given
496477 identity_document = map_identity_to_identity_document (identity )
497478 identity_model = IdentityModel .parse_obj (identity_document )
498- environment_document = map_environment_to_environment_document (environment )
499-
500- mocked_environment_wrapper = mocker .patch (
501- "environments.dynamodb.wrappers.identity_wrapper.DynamoEnvironmentWrapper"
502- )
503- mocked_environment_wrapper .return_value .get_item .return_value = environment_document
504479
505480 dynamo_identity_wrapper = DynamoIdentityWrapper ()
506481 mocker .patch .object (
@@ -514,6 +489,47 @@ def test_get_segment_ids_with_identity_model(identity, environment, mocker): #
514489 assert segment_ids == []
515490
516491
492+ def test_get_segment_ids__compressed_environment_in_dynamo__returns_correct_segment_ids (
493+ identity : "Identity" ,
494+ identity_matching_segment : "Segment" ,
495+ dynamodb_identity_wrapper : DynamoIdentityWrapper ,
496+ flagsmith_identities_table : Table ,
497+ flagsmith_environment_table : Table ,
498+ settings : "SettingsWrapper" ,
499+ ) -> None :
500+ """Regression test for https://github.com/Flagsmith/flagsmith/issues/6912
501+
502+ Previously, get_segment_ids read the environment document from DynamoDB
503+ and failed with a ValidationError when the document contained compressed
504+ (gzipped Binary) `project` and `feature_states` fields.
505+ """
506+ # Given - identity written to DynamoDB
507+ identity_document = map_identity_to_identity_document (identity )
508+ flagsmith_identities_table .put_item (Item = identity_document )
509+ identity_uuid = str (identity_document ["identity_uuid" ])
510+
511+ # And - a compressed environment document in DynamoDB
512+ settings .ENVIRONMENTS_TABLE_NAME_DYNAMO = flagsmith_environment_table .name
513+ compressed_result = map_environment_to_compressed_environment_document (
514+ identity .environment ,
515+ )
516+ flagsmith_environment_table .put_item (Item = compressed_result .document )
517+
518+ # Verify the document actually has compressed Binary fields
519+ stored = flagsmith_environment_table .get_item (
520+ Key = {"api_key" : identity .environment .api_key },
521+ )["Item" ]
522+ assert stored .get ("compressed" ) is True
523+ assert isinstance (stored ["project" ], Binary )
524+ assert isinstance (stored ["feature_states" ], Binary )
525+
526+ # When
527+ segment_ids = dynamodb_identity_wrapper .get_segment_ids (identity_uuid )
528+
529+ # Then
530+ assert segment_ids == [identity_matching_segment .id ]
531+
532+
517533def test_identity_wrapper__iter_all_items_paginated__returns_expected (
518534 identity : "Identity" ,
519535 mocker : "MockerFixture" ,
@@ -643,41 +659,6 @@ def test_identity_wrapper__iter_all_items_paginated__capacity_budget_set__raises
643659 )
644660
645661
646- def test_get_segment_ids__called_multiple_times__reuses_environment_wrapper (
647- project : "Project" ,
648- environment : "Environment" ,
649- identity : "Identity" ,
650- mocker : "MockerFixture" ,
651- ) -> None :
652- # Given
653- identity_document = map_identity_to_identity_document (identity )
654- environment_document = map_environment_to_environment_document (environment )
655-
656- mocked_environment_wrapper_class = mocker .patch (
657- "environments.dynamodb.wrappers.identity_wrapper.DynamoEnvironmentWrapper"
658- )
659- mocked_environment_wrapper_class .return_value .get_item .return_value = (
660- environment_document
661- )
662-
663- dynamo_identity_wrapper = DynamoIdentityWrapper ()
664- mocker .patch .object (
665- dynamo_identity_wrapper ,
666- "get_item_from_uuid" ,
667- return_value = identity_document ,
668- )
669- identity_uuid = str (identity_document ["identity_uuid" ])
670-
671- # When
672- dynamo_identity_wrapper .get_segment_ids (identity_uuid )
673- dynamo_identity_wrapper .get_segment_ids (identity_uuid )
674- dynamo_identity_wrapper .get_segment_ids (identity_uuid )
675-
676- # Then - DynamoEnvironmentWrapper should be instantiated once
677- # (during DynamoIdentityWrapper.__init__), not once per get_segment_ids call.
678- assert mocked_environment_wrapper_class .call_count == 1
679-
680-
681662def test_delete_all_identities__deletes_all_identities_documents_from_dynamodb (
682663 flagsmith_identities_table : Table ,
683664 dynamodb_identity_wrapper : DynamoIdentityWrapper ,
0 commit comments