1
+ using System ;
2
+ using System . Collections . Generic ;
1
3
using System . Text ;
2
4
using FluentAssertions ;
3
5
using QsNet . Enums ;
@@ -108,4 +110,111 @@ public void CopyWith_WithModifications_ShouldReturnModifiedOptions()
108
110
newOptions . ParseLists . Should ( ) . BeTrue ( ) ;
109
111
newOptions . StrictNullHandling . Should ( ) . BeFalse ( ) ;
110
112
}
113
+
114
+ [ Fact ]
115
+ public void DecodeKey_ShouldThrow_When_DecodeDotInKeysTrue_And_AllowDotsFalse ( )
116
+ {
117
+ var options = new DecodeOptions
118
+ {
119
+ AllowDots = false ,
120
+ DecodeDotInKeys = true
121
+ } ;
122
+
123
+ Action act = ( ) => options . DecodeKey ( "a%2Eb" , Encoding . UTF8 ) ;
124
+ act . Should ( ) . Throw < ArgumentException > ( )
125
+ . Where ( e => e . Message . Contains ( "decodeDotInKeys" , StringComparison . OrdinalIgnoreCase )
126
+ && e . Message . Contains ( "allowDots" , StringComparison . OrdinalIgnoreCase ) ) ;
127
+ }
128
+
129
+ [ Fact ]
130
+ public void DecodeKey_DecodesPercentSequences_LikeValues ( )
131
+ {
132
+ var options = new DecodeOptions
133
+ {
134
+ AllowDots = true ,
135
+ DecodeDotInKeys = false
136
+ } ;
137
+
138
+ options . DecodeKey ( "a%2Eb" , Encoding . UTF8 ) . Should ( ) . Be ( "a.b" ) ;
139
+ options . DecodeKey ( "a%2eb" , Encoding . UTF8 ) . Should ( ) . Be ( "a.b" ) ;
140
+ }
141
+
142
+ [ Fact ]
143
+ public void DecodeValue_DecodesPercentSequences_Normally ( )
144
+ {
145
+ var options = new DecodeOptions ( ) ;
146
+ options . DecodeValue ( "%2E" , Encoding . UTF8 ) . Should ( ) . Be ( "." ) ;
147
+ }
148
+
149
+ [ Fact ]
150
+ public void DecoderWithKind_IsUsed_For_Key_And_Value ( )
151
+ {
152
+ var calls = new List < ( string ? s , DecodeKind kind ) > ( ) ;
153
+ var options = new DecodeOptions
154
+ {
155
+ DecoderWithKind = ( s , enc , kind ) =>
156
+ {
157
+ calls . Add ( ( s , kind ) ) ;
158
+ return s ;
159
+ }
160
+ } ;
161
+
162
+ options . DecodeKey ( "x" , Encoding . UTF8 ) . Should ( ) . Be ( "x" ) ;
163
+ options . DecodeValue ( "y" , Encoding . UTF8 ) . Should ( ) . Be ( "y" ) ;
164
+
165
+ calls . Should ( ) . HaveCount ( 2 ) ;
166
+ calls [ 0 ] . kind . Should ( ) . Be ( DecodeKind . Key ) ;
167
+ calls [ 0 ] . s . Should ( ) . Be ( "x" ) ;
168
+ calls [ 1 ] . kind . Should ( ) . Be ( DecodeKind . Value ) ;
169
+ calls [ 1 ] . s . Should ( ) . Be ( "y" ) ;
170
+ }
171
+
172
+ [ Fact ]
173
+ public void DecoderWithKind_NullReturn_IsHonored_NoFallback ( )
174
+ {
175
+ var options = new DecodeOptions
176
+ {
177
+ DecoderWithKind = ( s , enc , kind ) => null
178
+ } ;
179
+
180
+ options . DecodeValue ( "foo" , Encoding . UTF8 ) . Should ( ) . BeNull ( ) ;
181
+ options . DecodeKey ( "bar" , Encoding . UTF8 ) . Should ( ) . BeNull ( ) ;
182
+ }
183
+
184
+ [ Fact ]
185
+ public void LegacyDecoder_IsUsed_When_NoKindAwareDecoder_IsProvided ( )
186
+ {
187
+ var options = new DecodeOptions
188
+ {
189
+ Decoder = ( s , enc ) => s is null ? null : s . ToUpperInvariant ( )
190
+ } ;
191
+
192
+ options . DecodeValue ( "abc" , Encoding . UTF8 ) . Should ( ) . Be ( "ABC" ) ;
193
+ // For keys, legacy decoder is also used when no kind-aware decoder is set
194
+ options . DecodeKey ( "a%2Eb" , Encoding . UTF8 ) . Should ( ) . Be ( "A%2EB" ) ;
195
+ }
196
+
197
+ [ Fact ]
198
+ public void CopyWith_PreservesAndOverrides_Decoders ( )
199
+ {
200
+ var original = new DecodeOptions
201
+ {
202
+ Decoder = ( s , enc ) => s == null ? null : $ "L:{ s } ",
203
+ DecoderWithKind = ( s , enc , k ) => s == null ? null : $ "K:{ k } :{ s } "
204
+ } ;
205
+
206
+ // Copy without overrides preserves both decoders
207
+ var copy = original . CopyWith ( ) ;
208
+ copy . DecodeValue ( "v" , Encoding . UTF8 ) . Should ( ) . Be ( "K:Value:v" ) ;
209
+ copy . DecodeKey ( "k" , Encoding . UTF8 ) . Should ( ) . Be ( "K:Key:k" ) ;
210
+
211
+ // Override only the legacy decoder; kind-aware remains
212
+ var copy2 = original . CopyWith ( decoder : ( s , enc ) => s == null ? null : $ "L2:{ s } ") ;
213
+ copy2 . DecodeValue ( "v" , Encoding . UTF8 ) . Should ( ) . Be ( "K:Value:v" ) ; // still kind-aware takes precedence
214
+
215
+ // Override kind-aware decoder
216
+ var copy3 = original . CopyWith ( decoderWithKind : ( s , enc , k ) => s == null ? null : $ "K2:{ k } :{ s } ") ;
217
+ copy3 . DecodeValue ( "v" , Encoding . UTF8 ) . Should ( ) . Be ( "K2:Value:v" ) ;
218
+ copy3 . DecodeKey ( "k" , Encoding . UTF8 ) . Should ( ) . Be ( "K2:Key:k" ) ;
219
+ }
111
220
}
0 commit comments