@@ -18,10 +18,15 @@ package runner
1818
1919import (
2020 "context"
21+ "os"
2122 "testing"
2223
2324 "github.com/stretchr/testify/require"
2425
26+ "github.com/llm-d/llm-d-router/pkg/epp/datastore"
27+ "github.com/llm-d/llm-d-router/pkg/epp/flowcontrol"
28+ "github.com/llm-d/llm-d-router/pkg/epp/flowcontrol/contracts"
29+ "github.com/llm-d/llm-d-router/pkg/epp/requestcontrol"
2530 runserver "github.com/llm-d/llm-d-router/pkg/epp/server"
2631)
2732
@@ -50,3 +55,102 @@ featureGates:
5055 require .False (t , r .featureGates [runserver .HAPopulateNonLeaderDatastoreFeatureGate ])
5156 })
5257}
58+
59+ // TestFlowControlFeatureGateAdmissionControlWiring exercises the flowControl feature gate through
60+ // the production config path (parseConfigurationPhaseOne -> parseConfigurationPhaseTwo ->
61+ // initAdmissionControl) in both directions:
62+ // - gate on: the FlowControlAdmissionController is wired, the loader emits a non-nil
63+ // FlowControlConfig, and the flow registry is exposed as the priority band control plane;
64+ // - gate off: the LegacyAdmissionController is wired and no flow control config is built.
65+ //
66+ // The "no featureGates stanza" case reads the gate's registered default from the parsed
67+ // feature-gate map instead of hardcoding it, so this test keeps passing unchanged when the gate
68+ // flips to enabled-by-default (#2104) and pins that the flip actually changes the default wiring.
69+ func TestFlowControlFeatureGateAdmissionControlWiring (t * testing.T ) {
70+ // The deprecated ENABLE_EXPERIMENTAL_FLOW_CONTROL_LAYER env var appends the gate to the config
71+ // during phase two; clear it so only the featureGates stanza under test drives the outcome.
72+ if v , ok := os .LookupEnv (enableExperimentalFlowControlLayer ); ok {
73+ require .NoError (t , os .Unsetenv (enableExperimentalFlowControlLayer ))
74+ t .Cleanup (func () { _ = os .Setenv (enableExperimentalFlowControlLayer , v ) })
75+ }
76+
77+ boolPtr := func (b bool ) * bool { return & b }
78+ testCases := []struct {
79+ name string
80+ configText string
81+ // wantEnabled nil means "expect whatever default the runner registered for the gate",
82+ // read programmatically from the feature gates parsed out of the stanza-less config.
83+ wantEnabled * bool
84+ }{
85+ {
86+ name : "no featureGates stanza follows the registered default" ,
87+ configText : `apiVersion: llm-d.ai/v1alpha1
88+ kind: EndpointPickerConfig
89+ ` ,
90+ wantEnabled : nil ,
91+ },
92+ {
93+ name : "flowControl gate enabled wires the flow control admission controller" ,
94+ configText : `apiVersion: llm-d.ai/v1alpha1
95+ kind: EndpointPickerConfig
96+ featureGates:
97+ - flowControl
98+ ` ,
99+ wantEnabled : boolPtr (true ),
100+ },
101+ {
102+ name : "flowControl=false restores the legacy admission controller" ,
103+ configText : `apiVersion: llm-d.ai/v1alpha1
104+ kind: EndpointPickerConfig
105+ featureGates:
106+ - flowControl=false
107+ ` ,
108+ wantEnabled : boolPtr (false ),
109+ },
110+ }
111+
112+ for _ , tc := range testCases {
113+ t .Run (tc .name , func (t * testing.T ) {
114+ ctx , cancel := context .WithCancel (context .Background ())
115+ defer cancel ()
116+
117+ opts := runserver .NewOptions ()
118+ opts .ConfigText = tc .configText
119+ opts .PoolName = "test-pool"
120+
121+ r := NewRunner ()
122+ rawConfig , err := r .parseConfigurationPhaseOne (ctx , opts )
123+ require .NoError (t , err )
124+
125+ wantEnabled := r .featureGates [flowcontrol .FeatureGate ] // Registered default.
126+ if tc .wantEnabled != nil {
127+ wantEnabled = * tc .wantEnabled
128+ require .Equal (t , wantEnabled , r .featureGates [flowcontrol .FeatureGate ],
129+ "the loader should honor the explicit featureGates stanza" )
130+ }
131+
132+ ds := datastore .NewDatastore (ctx , r .setupMetricsCollection (opts ))
133+ eppConfig , err := r .parseConfigurationPhaseTwo (ctx , rawConfig , ds )
134+ require .NoError (t , err )
135+
136+ endpointCandidates := contracts .EndpointCandidates (
137+ requestcontrol .NewDatastoreEndpointCandidates (ds ))
138+ _ , admissionController , controlPlane :=
139+ r .initAdmissionControl (ctx , opts , eppConfig , endpointCandidates )
140+
141+ if wantEnabled {
142+ require .IsType (t , & requestcontrol.FlowControlAdmissionController {}, admissionController )
143+ require .NotNil (t , eppConfig .FlowControlConfig ,
144+ "the loader should build a flow control config when the gate is on" )
145+ require .NotNil (t , controlPlane ,
146+ "the flow registry should be exposed as the priority band control plane" )
147+ } else {
148+ require .IsType (t , & requestcontrol.LegacyAdmissionController {}, admissionController )
149+ require .Nil (t , eppConfig .FlowControlConfig ,
150+ "the loader should not build a flow control config when the gate is off" )
151+ require .Nil (t , controlPlane ,
152+ "no priority band control plane should exist when the gate is off" )
153+ }
154+ })
155+ }
156+ }
0 commit comments