@@ -176,6 +176,66 @@ func TestShouldSkipRetryAfterChannelAffinityFailure(t *testing.T) {
176176 }
177177}
178178
179+ func TestExtractChannelAffinityValue_RequestHeader (t * testing.T ) {
180+ rec := httptest .NewRecorder ()
181+ ctx , _ := gin .CreateTestContext (rec )
182+ ctx .Request = httptest .NewRequest (http .MethodPost , "/v1/responses" , nil )
183+ ctx .Request .Header .Set ("X-Affinity-Key" , " tenant-123 " )
184+
185+ value := extractChannelAffinityValue (ctx , operation_setting.ChannelAffinityKeySource {
186+ Type : "request_header" ,
187+ Key : "X-Affinity-Key" ,
188+ })
189+
190+ require .Equal (t , "tenant-123" , value )
191+ }
192+
193+ func TestGetPreferredChannelByAffinity_RequestHeaderKeySource (t * testing.T ) {
194+ gin .SetMode (gin .TestMode )
195+
196+ rule := operation_setting.ChannelAffinityRule {
197+ Name : "header-affinity" ,
198+ ModelRegex : []string {"^gpt-.*$" },
199+ PathRegex : []string {"/v1/responses" },
200+ KeySources : []operation_setting.ChannelAffinityKeySource {
201+ {Type : "request_header" , Key : "X-Affinity-Key" },
202+ },
203+ IncludeRuleName : true ,
204+ IncludeModelName : true ,
205+ }
206+
207+ affinityValue := fmt .Sprintf ("header-hit-%d" , time .Now ().UnixNano ())
208+ cacheKeySuffix := buildChannelAffinityCacheKeySuffix (rule , "gpt-5" , "default" , affinityValue )
209+
210+ cache := getChannelAffinityCache ()
211+ require .NoError (t , cache .SetWithTTL (cacheKeySuffix , 9528 , time .Minute ))
212+ t .Cleanup (func () {
213+ _ , _ = cache .DeleteMany ([]string {cacheKeySuffix })
214+ })
215+
216+ setting := operation_setting .GetChannelAffinitySetting ()
217+ originalRules := setting .Rules
218+ setting .Rules = append ([]operation_setting.ChannelAffinityRule {rule }, originalRules ... )
219+ t .Cleanup (func () {
220+ setting .Rules = originalRules
221+ })
222+
223+ rec := httptest .NewRecorder ()
224+ ctx , _ := gin .CreateTestContext (rec )
225+ ctx .Request = httptest .NewRequest (http .MethodPost , "/v1/responses" , nil )
226+ ctx .Request .Header .Set ("X-Affinity-Key" , affinityValue )
227+
228+ channelID , found := GetPreferredChannelByAffinity (ctx , "gpt-5" , "default" )
229+ require .True (t , found )
230+ require .Equal (t , 9528 , channelID )
231+
232+ meta , ok := getChannelAffinityMeta (ctx )
233+ require .True (t , ok )
234+ require .Equal (t , "request_header" , meta .KeySourceType )
235+ require .Equal (t , "X-Affinity-Key" , meta .KeySourceKey )
236+ require .Equal (t , buildChannelAffinityKeyHint (affinityValue ), meta .KeyHint )
237+ }
238+
179239func TestChannelAffinityHitCodexTemplatePassHeadersEffective (t * testing.T ) {
180240 gin .SetMode (gin .TestMode )
181241
0 commit comments