@@ -49,22 +49,29 @@ public void AuthorizationHeaderShouldNotBeSetWithoutCredentials()
4949 }
5050
5151 [ Theory ]
52- [ InlineData ( "tenant123" , true ) ]
53- [ InlineData ( "tenant-123" , true ) ]
54- [ InlineData ( "tenant..123" , false ) ]
55- [ InlineData ( "." , false ) ]
56- [ InlineData ( "tenant!_*.123'()" , true ) ]
57- [ InlineData ( "tenant-123..." , false ) ]
58- [ InlineData ( "tenant123456...test" , false ) ]
59- [ InlineData ( "tenant1234567890!@" , false ) ]
52+ [ InlineData ( "tenant123" , true ) ] // only alphanumeric
53+ [ InlineData ( "tenant-123" , true ) ] // allowed hyphen
54+ [ InlineData ( "tenant..123" , false ) ] // double period not allowed
55+ [ InlineData ( "." , false ) ] // single period not allowed
56+ [ InlineData ( "tenant!_*.123'()" , true ) ] // allowed special characters
57+ [ InlineData ( "tenant-123..." , false ) ] // ends with multiple periods
58+ [ InlineData ( "tenant123456...test" , false ) ] // ends with period
59+ [ InlineData ( "tenant1234567890!@" , false ) ] // '@' is not allowed
60+ [ InlineData ( "a" , true ) ] // minimal length
61+ [ InlineData ( "tenant_with_underscores" , true ) ] // underscores
62+ [ InlineData ( "tenant.." , false ) ] // ends with double period
63+ [ InlineData ( "..tenant" , false ) ] // starts with double period
64+ [ InlineData ( "tenant-.-test" , true ) ] // single periods inside are ok
6065 public void TenantHeaderShouldBeCorrect ( string tenantId , bool isValid )
6166 {
6267 using var client = new TestLokiHttpClient ( ) ;
6368
6469 if ( isValid )
6570 {
71+ // Act
6672 client . SetTenant ( tenantId ) ;
6773
74+ // Assert header is correctly set
6875 var tenantHeaders = client . Client . DefaultRequestHeaders
6976 . GetValues ( "X-Scope-OrgID" )
7077 . ToList ( ) ;
@@ -73,6 +80,62 @@ public void TenantHeaderShouldBeCorrect(string tenantId, bool isValid)
7380 }
7481 else
7582 {
83+ // Act & Assert: invalid tenant IDs throw ArgumentException
84+ Should . Throw < ArgumentException > ( ( ) => client . SetTenant ( tenantId ) ) ;
85+ }
86+ }
87+
88+ // Allowed special characters
89+ [ Theory ]
90+ [ InlineData ( '!' , true ) ]
91+ [ InlineData ( '.' , true ) ]
92+ [ InlineData ( '_' , true ) ]
93+ [ InlineData ( '*' , true ) ]
94+ [ InlineData ( '\' ' , true ) ]
95+ [ InlineData ( '(' , true ) ]
96+ [ InlineData ( ')' , true ) ]
97+ [ InlineData ( '-' , true ) ]
98+
99+ // Disallowed special characters
100+ [ InlineData ( '@' , false ) ]
101+ [ InlineData ( '#' , false ) ]
102+ [ InlineData ( '&' , false ) ]
103+ [ InlineData ( '$' , false ) ]
104+ [ InlineData ( '%' , false ) ]
105+ [ InlineData ( '^' , false ) ]
106+ [ InlineData ( '=' , false ) ]
107+ [ InlineData ( '+' , false ) ]
108+ [ InlineData ( '[' , false ) ]
109+ [ InlineData ( ']' , false ) ]
110+ [ InlineData ( '{' , false ) ]
111+ [ InlineData ( '}' , false ) ]
112+ [ InlineData ( '<' , false ) ]
113+ [ InlineData ( '>' , false ) ]
114+ [ InlineData ( '?' , false ) ]
115+ [ InlineData ( '/' , false ) ]
116+ [ InlineData ( '\\ ' , false ) ]
117+ [ InlineData ( '|' , false ) ]
118+ [ InlineData ( '~' , false ) ]
119+ [ InlineData ( '"' , false ) ]
120+ public void TenantSpecialCharacterShouldValidateCorrectly ( char specialChar , bool isValid )
121+ {
122+ using var client = new TestLokiHttpClient ( ) ;
123+ string tenantId = "tenant" + specialChar + "123" ;
124+
125+ if ( isValid )
126+ {
127+ // Should succeed
128+ client . SetTenant ( tenantId ) ;
129+
130+ var tenantHeaders = client . Client . DefaultRequestHeaders
131+ . GetValues ( "X-Scope-OrgID" )
132+ . ToList ( ) ;
133+
134+ tenantHeaders . ShouldBeEquivalentTo ( new List < string > { tenantId } ) ;
135+ }
136+ else
137+ {
138+ // Should throw
76139 Should . Throw < ArgumentException > ( ( ) => client . SetTenant ( tenantId ) ) ;
77140 }
78141 }
@@ -96,23 +159,109 @@ public void TenantHeaderShouldThrowAnExceptionOnTenantIdAgainstRule()
96159 Should . Throw < ArgumentException > ( ( ) => client . SetTenant ( tenantId ) ) ;
97160 }
98161
99- [ Fact ]
100- public void SetDefaultHeadersShouldSetHeaderCorrectly ( )
162+ [ Theory ]
163+ [ InlineData ( "Custom-Header" , "HeaderValue" , true ) ]
164+ [ InlineData ( "X-Test" , "12345" , true ) ]
165+ [ InlineData ( "X-Correlation-ID" , "abcd-1234" , true ) ]
166+ [ InlineData ( "X-Feature-Flag" , "enabled" , true ) ]
167+ [ InlineData ( "" , "value" , false ) ]
168+ [ InlineData ( " " , "value" , false ) ]
169+ [ InlineData ( null , "value" , false ) ]
170+ [ InlineData ( "Invalid Header" , "value" , false ) ]
171+ [ InlineData ( "X-Test" , "" , false ) ]
172+ [ InlineData ( "X-Test" , null , false ) ]
173+ public void SetDefaultHeadersShouldValidateCorrectly ( string headerKey , string headerValue , bool isValid )
174+ {
175+ using var httpClient = new HttpClient ( ) ;
176+ var client = new TestLokiHttpClient ( httpClient ) ;
177+
178+ if ( isValid )
179+ {
180+ var headersToSet = new Dictionary < string , string >
181+ {
182+ { headerKey , headerValue }
183+ } ;
184+
185+ client . SetDefaultHeaders ( headersToSet ) ;
186+
187+ httpClient . DefaultRequestHeaders . Contains ( headerKey ) . ShouldBeTrue ( ) ;
188+ httpClient . DefaultRequestHeaders
189+ . GetValues ( headerKey )
190+ . ShouldBe ( new [ ] { headerValue } ) ;
191+ }
192+ else
193+ {
194+ Should . Throw < ArgumentException > ( ( ) =>
195+ {
196+ var headersToSet = new Dictionary < string , string >
197+ {
198+ { headerKey , headerValue }
199+ } ;
200+ client . SetDefaultHeaders ( headersToSet ) ;
201+ } ) ;
202+ }
203+ }
204+
205+ [ Theory ]
206+ [ InlineData ( '!' , true ) ]
207+ [ InlineData ( '#' , true ) ]
208+ [ InlineData ( '$' , true ) ]
209+ [ InlineData ( '%' , true ) ]
210+ [ InlineData ( '&' , true ) ]
211+ [ InlineData ( '\' ' , true ) ]
212+ [ InlineData ( '*' , true ) ]
213+ [ InlineData ( '+' , true ) ]
214+ [ InlineData ( '-' , true ) ]
215+ [ InlineData ( '.' , true ) ]
216+ [ InlineData ( '^' , true ) ]
217+ [ InlineData ( '_' , true ) ]
218+ [ InlineData ( '`' , true ) ]
219+ [ InlineData ( '|' , true ) ]
220+ [ InlineData ( '~' , true ) ]
221+ [ InlineData ( 'A' , true ) ]
222+ [ InlineData ( 'z' , true ) ]
223+ [ InlineData ( ' ' , false ) ]
224+ [ InlineData ( '(' , false ) ]
225+ [ InlineData ( ')' , false ) ]
226+ [ InlineData ( '<' , false ) ]
227+ [ InlineData ( '>' , false ) ]
228+ [ InlineData ( '@' , false ) ]
229+ [ InlineData ( ',' , false ) ]
230+ [ InlineData ( ';' , false ) ]
231+ [ InlineData ( ':' , false ) ]
232+ [ InlineData ( '"' , false ) ]
233+ [ InlineData ( '/' , false ) ]
234+ [ InlineData ( '[' , false ) ]
235+ [ InlineData ( ']' , false ) ]
236+ [ InlineData ( '?' , false ) ]
237+ [ InlineData ( '=' , false ) ]
238+ [ InlineData ( '{' , false ) ]
239+ [ InlineData ( '}' , false ) ]
240+ [ InlineData ( '\\ ' , false ) ]
241+ [ InlineData ( '\t ' , false ) ]
242+ public void DefaultHeaderCharactersShouldValidateCorrectly ( char character , bool isValid ) // Valid token characters according to RFC 7230
101243 {
102- // Arrange
103244 using var httpClient = new HttpClient ( ) ;
104245 var client = new TestLokiHttpClient ( httpClient ) ;
105246
247+ string headerKey = "X-Test" + character ;
106248 var headersToSet = new Dictionary < string , string >
107249 {
108- { "Custom-Header" , "HeaderValue " }
250+ { headerKey , "value " }
109251 } ;
110252
111- // Act
112- client . SetDefaultHeaders ( headersToSet ) ;
253+ if ( isValid )
254+ {
255+ // Should succeed
256+ client . SetDefaultHeaders ( headersToSet ) ;
113257
114- // Assert
115- httpClient . DefaultRequestHeaders . Contains ( "Custom-Header" ) . ShouldBeTrue ( ) ;
116- httpClient . DefaultRequestHeaders . GetValues ( "Custom-Header" ) . ShouldBe ( new [ ] { "HeaderValue" } ) ;
258+ httpClient . DefaultRequestHeaders . Contains ( headerKey ) . ShouldBeTrue ( ) ;
259+ httpClient . DefaultRequestHeaders . GetValues ( headerKey ) . ShouldBe ( new [ ] { "value" } ) ;
260+ }
261+ else
262+ {
263+ // Should throw exception
264+ Should . Throw < ArgumentException > ( ( ) => client . SetDefaultHeaders ( headersToSet ) ) ;
265+ }
117266 }
118267}
0 commit comments