@@ -26,10 +26,12 @@ import (
2626 "k8s.io/utils/ptr"
2727
2828 configapi "sigs.k8s.io/gateway-api-inference-extension/apix/config/v1alpha1"
29+ flowcontrolif "sigs.k8s.io/gateway-api-inference-extension/pkg/epp/framework/interface/flowcontrol"
2930 "sigs.k8s.io/gateway-api-inference-extension/pkg/epp/framework/interface/flowcontrol/mocks"
3031 fwkplugin "sigs.k8s.io/gateway-api-inference-extension/pkg/epp/framework/interface/plugin"
3132 "sigs.k8s.io/gateway-api-inference-extension/pkg/epp/framework/plugins/flowcontrol/fairness"
3233 "sigs.k8s.io/gateway-api-inference-extension/pkg/epp/framework/plugins/flowcontrol/ordering"
34+ "sigs.k8s.io/gateway-api-inference-extension/pkg/epp/framework/plugins/flowcontrol/usagelimits"
3335 "sigs.k8s.io/gateway-api-inference-extension/test/utils"
3436)
3537
@@ -49,6 +51,23 @@ func TestNewConfigFromAPI(t *testing.T) {
4951 Type : ordering .FCFSOrderingPolicyType ,
5052 },
5153 })
54+ handle .AddPlugin (usagelimits .StaticUsageLimitPolicyType , usagelimits .DefaultPolicy ())
55+
56+ // A func-based custom policy that always returns 0.8 — demonstrates that users can define
57+ // their policy in the standard plugins section and reference it via UsageLimit.PluginRef.
58+ const funcPolicyName = "func-policy"
59+ handle .AddPlugin (funcPolicyName , usagelimits .NewPolicyFunc (funcPolicyName , func (_ context.Context , _ float64 , priorities []int ) []float64 {
60+ result := make ([]float64 , len (priorities ))
61+ for i := range result {
62+ result [i ] = 0.8
63+ }
64+ return result
65+ }))
66+
67+ // A hand-rolled struct implementing UsageLimitPolicy — demonstrates that any struct
68+ // satisfying the interface can be registered and resolved, without relying on usagelimits helpers.
69+ const structPolicyName = "struct-policy"
70+ handle .AddPlugin (structPolicyName , & constantPointEightPolicy {})
5271
5372 testCases := []struct {
5473 name string
@@ -79,6 +98,70 @@ func TestNewConfigFromAPI(t *testing.T) {
7998 "MaxBytes should be correctly translated from resource.Quantity in API to uint64 in internal config" )
8099 },
81100 },
101+ {
102+ name : "Success - Default UsageLimitPolicy when UsageLimit is nil" ,
103+ apiConfig : nil ,
104+ assertion : func (t * testing.T , cfg * Config ) {
105+ require .NotNil (t , cfg .UsageLimitPolicy , "UsageLimitPolicy should be resolved even when not explicitly configured" )
106+ ceilings := cfg .UsageLimitPolicy .ComputeLimit (context .Background (), 0.5 , []int {0 })
107+ assert .Equal (t , []float64 {1.0 }, ceilings , "Default noop policy should return 1.0 (no gating)" )
108+ },
109+ },
110+ {
111+ name : "Success - UsageLimitPolicyPluginRef is resolved" ,
112+ apiConfig : & configapi.FlowControlConfig {
113+ UsageLimitPolicyPluginRef : usagelimits .StaticUsageLimitPolicyType ,
114+ },
115+ assertion : func (t * testing.T , cfg * Config ) {
116+ require .NotNil (t , cfg .UsageLimitPolicy , "UsageLimitPolicy should be resolved from the handle" )
117+ ceilings := cfg .UsageLimitPolicy .ComputeLimit (context .Background (), 0.5 , []int {0 })
118+ assert .Equal (t , []float64 {1.0 }, ceilings , "Noop policy should return 1.0 (no gating)" )
119+ },
120+ },
121+ {
122+ name : "Success - Func-based UsageLimitPolicy resolved via PluginRef" ,
123+ apiConfig : & configapi.FlowControlConfig {
124+ UsageLimitPolicyPluginRef : funcPolicyName ,
125+ },
126+ assertion : func (t * testing.T , cfg * Config ) {
127+ require .NotNil (t , cfg .UsageLimitPolicy )
128+ ctx := context .Background ()
129+ for _ , tc := range []struct {
130+ name string
131+ priority int
132+ saturation float64
133+ }{
134+ {"zero saturation" , 0 , 0.0 },
135+ {"half saturation" , 1 , 0.5 },
136+ {"full saturation" , 5 , 1.0 },
137+ } {
138+ assert .Equal (t , []float64 {0.8 }, cfg .UsageLimitPolicy .ComputeLimit (ctx , tc .saturation , []int {tc .priority }),
139+ "func-based policy should return 0.8 at %s" , tc .name )
140+ }
141+ },
142+ },
143+ {
144+ name : "Success - Struct-based UsageLimitPolicy resolved via PluginRef" ,
145+ apiConfig : & configapi.FlowControlConfig {
146+ UsageLimitPolicyPluginRef : structPolicyName ,
147+ },
148+ assertion : func (t * testing.T , cfg * Config ) {
149+ require .NotNil (t , cfg .UsageLimitPolicy )
150+ ctx := context .Background ()
151+ for _ , tc := range []struct {
152+ name string
153+ priority int
154+ saturation float64
155+ }{
156+ {"zero saturation" , 0 , 0.0 },
157+ {"half saturation" , 1 , 0.5 },
158+ {"full saturation" , 5 , 1.0 },
159+ } {
160+ assert .Equal (t , []float64 {0.8 }, cfg .UsageLimitPolicy .ComputeLimit (ctx , tc .saturation , []int {tc .priority }),
161+ "struct-based policy should return 0.8 at %s" , tc .name )
162+ }
163+ },
164+ },
82165 }
83166
84167 for _ , tc := range testCases {
@@ -96,3 +179,26 @@ func TestNewConfigFromAPI(t *testing.T) {
96179 })
97180 }
98181}
182+
183+ // constantPointEightPolicy is a hand-rolled UsageLimitPolicy implementation that always returns 0.8.
184+ // It exists to show that any struct satisfying the interface can be registered and resolved,
185+ // without relying on the usagelimits.NewPolicyFunc helper.
186+ type constantPointEightPolicy struct {}
187+
188+ func (p * constantPointEightPolicy ) TypedName () fwkplugin.TypedName {
189+ return fwkplugin.TypedName {
190+ Type : "constant-point-eight-policy-type" ,
191+ Name : "constant-point-eight-policy" ,
192+ }
193+ }
194+
195+ func (p * constantPointEightPolicy ) ComputeLimit (_ context.Context , _ float64 , priorities []int ) []float64 {
196+ result := make ([]float64 , len (priorities ))
197+ for i := range result {
198+ result [i ] = 0.8
199+ }
200+ return result
201+ }
202+
203+ // compile-time check that constantPointEightPolicy satisfies the interface.
204+ var _ flowcontrolif.UsageLimitPolicy = (* constantPointEightPolicy )(nil )
0 commit comments