1
+ """
2
+ Tests for GeoDjango binary parameter handling fix
3
+ """
4
+ import json
5
+ import base64
6
+ import unittest
7
+
8
+ from debug_toolbar .panels .sql .forms import _reconstruct_params
9
+ from debug_toolbar .panels .sql .tracking import NormalCursorMixin
10
+
11
+ from ..base import BaseTestCase
12
+
13
+
14
+ class MockCursor :
15
+ """Mock cursor for testing"""
16
+ pass
17
+
18
+
19
+ class MockConnection :
20
+ """Mock database connection for testing"""
21
+ vendor = "postgresql"
22
+ alias = "default"
23
+
24
+
25
+ class MockLogger :
26
+ """Mock logger for testing"""
27
+ def record (self , ** kwargs ):
28
+ pass
29
+
30
+
31
+ class TestCursor (NormalCursorMixin ):
32
+ """Test cursor that can be instantiated"""
33
+ def __init__ (self ):
34
+ # Initialize with mock objects
35
+ self .cursor = MockCursor ()
36
+ self .db = MockConnection ()
37
+ self .logger = MockLogger ()
38
+
39
+
40
+ class GeoDjangoBinaryParameterTest (BaseTestCase ):
41
+ """Test cases for GeoDjango binary parameter handling"""
42
+
43
+ def test_binary_parameter_encoding_decoding (self ):
44
+ """Test that binary parameters are properly encoded and decoded"""
45
+ # Create a test cursor with the _decode method
46
+ cursor = TestCursor ()
47
+
48
+ # Test binary data similar to GeoDjango EWKB geometry
49
+ binary_data = b'\x01 \x01 \x00 \x00 \x20 \xe6 \x10 \x00 \x00 \xff \xfe \xfd '
50
+
51
+ # Test encoding (what happens when query is logged)
52
+ encoded = cursor ._decode (binary_data )
53
+
54
+ # Should be marked as binary data
55
+ self .assertIsInstance (encoded , dict )
56
+ self .assertIn ("__djdt_binary__" , encoded )
57
+
58
+ # Should be base64 encoded
59
+ expected_b64 = base64 .b64encode (binary_data ).decode ('ascii' )
60
+ self .assertEqual (encoded ["__djdt_binary__" ], expected_b64 )
61
+
62
+ # Test JSON serialization (what happens in tracking.py)
63
+ json_params = json .dumps ([encoded ])
64
+
65
+ # Test parsing back from JSON
66
+ parsed = json .loads (json_params )
67
+
68
+ # Test reconstruction (what happens in forms.py)
69
+ reconstructed = _reconstruct_params (parsed )
70
+
71
+ # Should recover original binary data
72
+ self .assertEqual (len (reconstructed ), 1 )
73
+ self .assertEqual (reconstructed [0 ], binary_data )
74
+ self .assertIsInstance (reconstructed [0 ], bytes )
75
+
76
+ def test_mixed_parameter_types (self ):
77
+ """Test that mixed parameter types are handled correctly"""
78
+ cursor = TestCursor ()
79
+
80
+ # Test with mixed types including binary data
81
+ params = [
82
+ "string_param" ,
83
+ 42 ,
84
+ b'\x01 \x02 \x03 ' , # binary data
85
+ None ,
86
+ ["nested" , "list" ],
87
+ ]
88
+
89
+ # Encode each parameter
90
+ encoded_params = [cursor ._decode (p ) for p in params ]
91
+
92
+ # Serialize to JSON
93
+ json_str = json .dumps (encoded_params )
94
+
95
+ # Parse and reconstruct
96
+ parsed = json .loads (json_str )
97
+ reconstructed = _reconstruct_params (parsed )
98
+
99
+ # Check each parameter
100
+ self .assertEqual (reconstructed [0 ], "string_param" ) # string unchanged
101
+ self .assertEqual (reconstructed [1 ], 42 ) # int unchanged
102
+ self .assertEqual (reconstructed [2 ], b'\x01 \x02 \x03 ' ) # binary restored
103
+ self .assertIsNone (reconstructed [3 ]) # None unchanged
104
+ self .assertEqual (reconstructed [4 ], ["nested" , "list" ]) # list unchanged
105
+
106
+ def test_nested_binary_data (self ):
107
+ """Test binary data nested in lists and dicts"""
108
+ cursor = TestCursor ()
109
+
110
+ # Test nested structures with binary data
111
+ nested_params = [
112
+ [b'\x01 \x02 ' , "string" , b'\x03 \x04 ' ],
113
+ {"key" : b'\x05 \x06 ' , "other" : "value" },
114
+ ]
115
+
116
+ # Encode
117
+ encoded = [cursor ._decode (p ) for p in nested_params ]
118
+
119
+ # Serialize and parse
120
+ json_str = json .dumps (encoded )
121
+ parsed = json .loads (json_str )
122
+ reconstructed = _reconstruct_params (parsed )
123
+
124
+ # Check nested list
125
+ self .assertEqual (reconstructed [0 ][0 ], b'\x01 \x02 ' )
126
+ self .assertEqual (reconstructed [0 ][1 ], "string" )
127
+ self .assertEqual (reconstructed [0 ][2 ], b'\x03 \x04 ' )
128
+
129
+ # Check nested dict
130
+ self .assertEqual (reconstructed [1 ]["key" ], b'\x05 \x06 ' )
131
+ self .assertEqual (reconstructed [1 ]["other" ], "value" )
132
+
133
+ def test_empty_binary_data (self ):
134
+ """Test handling of empty binary data"""
135
+ cursor = TestCursor ()
136
+
137
+ # Test empty bytes
138
+ empty_bytes = b''
139
+ encoded = cursor ._decode (empty_bytes )
140
+
141
+ # Should still be marked as binary
142
+ self .assertIsInstance (encoded , dict )
143
+ self .assertIn ("__djdt_binary__" , encoded )
144
+
145
+ # Reconstruct
146
+ json_str = json .dumps ([encoded ])
147
+ parsed = json .loads (json_str )
148
+ reconstructed = _reconstruct_params (parsed )
149
+
150
+ self .assertEqual (reconstructed [0 ], empty_bytes )
151
+
152
+ def test_bytearray_support (self ):
153
+ """Test that bytearray is also handled as binary data"""
154
+ cursor = TestCursor ()
155
+
156
+ # Test bytearray
157
+ byte_array = bytearray (b'\x01 \x02 \x03 \x04 ' )
158
+ encoded = cursor ._decode (byte_array )
159
+
160
+ # Should be marked as binary
161
+ self .assertIn ("__djdt_binary__" , encoded )
162
+
163
+ # Reconstruct (should become bytes, not bytearray)
164
+ json_str = json .dumps ([encoded ])
165
+ parsed = json .loads (json_str )
166
+ reconstructed = _reconstruct_params (parsed )
167
+
168
+ # Should be equal in content (bytes vs bytearray comparison works)
169
+ self .assertEqual (reconstructed [0 ], byte_array )
170
+ # Should be bytes type after reconstruction
171
+ self .assertIsInstance (reconstructed [0 ], bytes )
0 commit comments