@@ -620,3 +620,183 @@ def test_get_tags_mutation(self):
620620 new_tags = tag_manager .get_tags ()
621621 assert new_tags == ['test_key:test_value' ]
622622 assert new_tags != tags # The lists should be different objects
623+
624+ # Normalization tests
625+ def mock_tag_normalizer (self , tag ):
626+ """Mock normalizer that replaces spaces and hyphens with underscores and lowercases"""
627+ return tag .replace (' ' , '_' ).replace ('-' , '_' ).lower ()
628+
629+ def test_init_with_normalizer (self ):
630+ """Test initialization of TagManager with normalizer"""
631+ tag_manager = TagManager (normalizer = self .mock_tag_normalizer )
632+ assert tag_manager ._normalizer == self .mock_tag_normalizer
633+ assert tag_manager ._tags == {}
634+ assert tag_manager ._cached_tag_list is None
635+
636+ def test_set_tag_with_normalization (self ):
637+ """Test setting tags with normalization enabled"""
638+ tag_manager = TagManager (normalizer = self .mock_tag_normalizer )
639+
640+ # Test with normalize=True - only value should be normalized, not key
641+ tag_manager .set_tag ('test-key' , 'value with spaces' , normalize = True )
642+ assert tag_manager ._tags == {'test-key' : ['value_with_spaces' ]}
643+
644+ # Test keyless tag normalization
645+ tag_manager .set_tag (None , 'keyless value' , normalize = True )
646+ assert TagType .KEYLESS in tag_manager ._tags
647+ assert tag_manager ._tags [TagType .KEYLESS ] == ['keyless_value' ]
648+
649+ def test_set_tag_without_normalization (self ):
650+ """Test setting tags without normalization"""
651+ tag_manager = TagManager (normalizer = self .mock_tag_normalizer )
652+
653+ # Test with normalize=False
654+ tag_manager .set_tag ('test-key' , 'value with spaces' , normalize = False )
655+ assert tag_manager ._tags == {'test-key' : ['value with spaces' ]}
656+
657+ # Test with no normalize parameter (should default to False)
658+ tag_manager .set_tag ('another-key' , 'another value' )
659+ assert tag_manager ._tags == {'test-key' : ['value with spaces' ], 'another-key' : ['another value' ]}
660+
661+ def test_set_tags_from_list_with_normalization (self ):
662+ """Test setting tags from list with normalization enabled"""
663+ tag_manager = TagManager (normalizer = self .mock_tag_normalizer )
664+
665+ tag_list = ['env:prod-test' , 'service:web app' , 'keyless-tag' ]
666+ tag_manager .set_tags_from_list (tag_list , normalize = True )
667+
668+ expected_tags = sorted (['env:prod_test' , 'service:web_app' , 'keyless_tag' ])
669+ assert sorted (tag_manager .get_tags ()) == expected_tags
670+
671+ def test_set_tags_from_list_without_normalization (self ):
672+ """Test setting tags from list without normalization"""
673+ tag_manager = TagManager (normalizer = self .mock_tag_normalizer )
674+
675+ tag_list = ['env:prod-test' , 'service:web app' , 'keyless-tag' ]
676+ tag_manager .set_tags_from_list (tag_list , normalize = False )
677+
678+ expected_tags = sorted (['env:prod-test' , 'service:web app' , 'keyless-tag' ])
679+ assert sorted (tag_manager .get_tags ()) == expected_tags
680+
681+ def test_delete_tag_with_normalization (self ):
682+ """Test deleting tags with normalization enabled"""
683+ tag_manager = TagManager (normalizer = self .mock_tag_normalizer )
684+
685+ # Set tag with normalization (only value normalized)
686+ tag_manager .set_tag ('test-key' , 'value with spaces' , normalize = True )
687+ assert 'test-key' in tag_manager ._tags
688+ assert tag_manager ._tags ['test-key' ] == ['value_with_spaces' ]
689+
690+ # Delete with normalization - should find the normalized value
691+ result = tag_manager .delete_tag ('test-key' , 'value with spaces' , normalize = True )
692+ assert result is True
693+ assert tag_manager ._tags == {}
694+
695+ def test_delete_tag_without_normalization (self ):
696+ """Test deleting tags without normalization"""
697+ tag_manager = TagManager (normalizer = self .mock_tag_normalizer )
698+
699+ # Set tag without normalization
700+ tag_manager .set_tag ('test-key' , 'value with spaces' , normalize = False )
701+ assert tag_manager ._tags == {'test-key' : ['value with spaces' ]}
702+
703+ # Delete without normalization
704+ result = tag_manager .delete_tag ('test-key' , 'value with spaces' , normalize = False )
705+ assert result is True
706+ assert tag_manager ._tags == {}
707+
708+ def test_delete_tag_normalization_mismatch (self ):
709+ """Test that deletion fails when normalization doesn't match storage"""
710+ tag_manager = TagManager (normalizer = self .mock_tag_normalizer )
711+
712+ # Set tag with normalization (only value normalized)
713+ tag_manager .set_tag ('test-key' , 'value with spaces' , normalize = True )
714+ assert tag_manager ._tags == {'test-key' : ['value_with_spaces' ]}
715+
716+ # Try to delete without normalization - should fail
717+ result = tag_manager .delete_tag ('test-key' , 'value with spaces' , normalize = False )
718+ assert result is False
719+ assert tag_manager ._tags == {'test-key' : ['value_with_spaces' ]}
720+
721+ # Delete with normalization - should succeed
722+ result = tag_manager .delete_tag ('test-key' , 'value with spaces' , normalize = True )
723+ assert result is True
724+ assert tag_manager ._tags == {}
725+
726+ def test_delete_entire_key_with_normalization (self ):
727+ """Test deleting entire key with normalization"""
728+ tag_manager = TagManager (normalizer = self .mock_tag_normalizer )
729+
730+ # Set multiple values for a key with normalization (only values normalized)
731+ tag_manager .set_tag ('test-key' , 'value1' , normalize = True )
732+ tag_manager .set_tag ('test-key' , 'value2' , normalize = True )
733+ assert tag_manager ._tags == {'test-key' : ['value1' , 'value2' ]}
734+
735+ # Delete entire key - key doesn't need normalization
736+ result = tag_manager .delete_tag ('test-key' , normalize = True )
737+ assert result is True
738+ assert tag_manager ._tags == {}
739+
740+ def test_normalization_with_no_normalizer (self ):
741+ """Test that normalize parameter is ignored when no normalizer is provided"""
742+ tag_manager = TagManager () # No normalizer
743+
744+ # Should work the same regardless of normalize parameter
745+ tag_manager .set_tag ('test-key' , 'value with spaces' , normalize = True )
746+ assert tag_manager ._tags == {'test-key' : ['value with spaces' ]}
747+
748+ tag_manager .set_tags_from_list (['env:prod-test' ], normalize = True )
749+ expected_tags = sorted (['test-key:value with spaces' , 'env:prod-test' ])
750+ assert sorted (tag_manager .get_tags ()) == expected_tags
751+
752+ # Delete should also work
753+ result = tag_manager .delete_tag ('test-key' , 'value with spaces' , normalize = True )
754+ assert result is True
755+
756+ def test_case_sensitivity_normalization (self ):
757+ """Test that normalization handles case sensitivity correctly (only values normalized)"""
758+ tag_manager = TagManager (normalizer = self .mock_tag_normalizer )
759+
760+ # Test only value gets normalized, key assumed to be lowercase already
761+ tag_manager .set_tag ('test_key' , 'UPPERCASE-VALUE' , normalize = True )
762+ assert tag_manager ._tags == {'test_key' : ['uppercase_value' ]}
763+
764+ # Test mixed case value normalization
765+ tag_manager .set_tag ('another_key' , 'SoMe VaLuE' , normalize = True )
766+ assert 'another_key' in tag_manager ._tags
767+ assert tag_manager ._tags ['another_key' ] == ['some_value' ]
768+
769+ # Verify final tags - keys lowercase, values normalized
770+ expected_tags = sorted (['test_key:uppercase_value' , 'another_key:some_value' ])
771+ assert sorted (tag_manager .get_tags ()) == expected_tags
772+
773+ # Test deletion with case sensitivity
774+ result = tag_manager .delete_tag ('test_key' , 'UPPERCASE-VALUE' , normalize = True )
775+ assert result is True
776+ assert tag_manager ._tags == {'another_key' : ['some_value' ]}
777+
778+ def test_case_sensitivity_in_tag_list (self ):
779+ """Test case sensitivity normalization in tag lists"""
780+ tag_manager = TagManager (normalizer = self .mock_tag_normalizer )
781+
782+ tag_list = ['env:PRODUCTION' , 'service:WEB-APP' , 'datacenter:US-EAST-1' , 'KEYLESS-TAG-UPPERCASE' ]
783+ tag_manager .set_tags_from_list (tag_list , normalize = True )
784+
785+ # When normalize_tag is applied to the full tag string, it normalizes everything
786+ expected_tags = sorted (['env:production' , 'service:web_app' , 'datacenter:us_east_1' , 'keyless_tag_uppercase' ])
787+ assert sorted (tag_manager .get_tags ()) == expected_tags
788+
789+ def test_case_sensitivity_without_normalization (self ):
790+ """Test that case sensitivity is preserved without normalization"""
791+ tag_manager = TagManager (normalizer = self .mock_tag_normalizer )
792+
793+ # Set tags without normalization - case should be preserved
794+ tag_manager .set_tag ('test_key' , 'UPPERCASE-VALUE' , normalize = False )
795+ assert tag_manager ._tags == {'test_key' : ['UPPERCASE-VALUE' ]}
796+
797+ # Set tag list without normalization - case should be preserved
798+ tag_list = ['env:PRODUCTION' , 'KEYLESS-TAG-UPPERCASE' ]
799+ tag_manager .set_tags_from_list (tag_list , normalize = False )
800+
801+ expected_tags = sorted (['test_key:UPPERCASE-VALUE' , 'env:PRODUCTION' , 'KEYLESS-TAG-UPPERCASE' ])
802+ assert sorted (tag_manager .get_tags ()) == expected_tags
0 commit comments