@@ -41,3 +41,48 @@ def test_returns_true_on_valid_btc_address(value: str):
4141def test_returns_failed_validation_on_invalid_btc_address (value : str ):
4242 """Test returns failed validation on invalid btc address."""
4343 assert isinstance (btc_address (value ), ValidationError )
44+
45+
46+ @pytest .mark .parametrize (
47+ "value" ,
48+ [
49+ "bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4" ,
50+ "tb1qw508d6qejxtdg4y5r3zarvary0c5xw7kxpjzsx" ,
51+ "bc1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3qccfmv3" ,
52+ "tb1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3q0sl5k7" ,
53+ ],
54+ )
55+ def test_segwit_accepts_uniform_case (value : str ):
56+ """Accept lowercase and uppercase mainnet and testnet SegWit addresses."""
57+ assert btc_address (value ) is True
58+ assert btc_address (value .upper ()) is True
59+
60+
61+ @pytest .mark .parametrize (
62+ "value" ,
63+ [
64+ "BC1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4" ,
65+ "bc1QW508D6QEJXTDG4Y5R3ZARVARY0C5XW7KV8F3T4" ,
66+ "bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4" .replace ("q" , "Q" , 1 ),
67+ "TB1qw508d6qejxtdg4y5r3zarvary0c5xw7kxpjzsx" ,
68+ "tb1QW508D6QEJXTDG4Y5R3ZARVARY0C5XW7KXPJZSX" ,
69+ "tb1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3q0sL5k7" ,
70+ ],
71+ )
72+ def test_segwit_rejects_mixed_case (value : str ):
73+ """Do not accept a case change confined to the prefix or data part."""
74+ assert isinstance (btc_address (value ), ValidationError )
75+
76+
77+ @pytest .mark .parametrize (
78+ "value" ,
79+ [
80+ "bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4\n " ,
81+ "BC1QW508D6QEJXTDG4Y5R3ZARVARY0C5XW7KV8F3T4\n " ,
82+ "bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4" .replace ("k" , "\u212a " ),
83+ "BC1QW508D6QEJXTDG4Y5R3ZARVARY0C5XW7KV8F3T4" .replace ("K" , "\u212a " ),
84+ ],
85+ )
86+ def test_segwit_rejects_non_ascii_and_trailing_characters (value : str ):
87+ """Case normalization must not admit Unicode lookalikes or trailing text."""
88+ assert isinstance (btc_address (value ), ValidationError )
0 commit comments