@@ -349,6 +349,32 @@ func TestLabelSelectorFilterEdgeCases(t *testing.T) {
349349 })
350350}
351351
352+ // TestLabelSelectorFilterHasNoAllowsNoLabelEscapeHatch documents and verifies the
353+ // property a role-scoped deployment relies on when it shares one InferencePool
354+ // across roles (rather than one InferencePool per role): unlike the decode-filter
355+ // shorthand (NewDecodeRole, allowsNoLabel=true), label-selector-filter's
356+ // matchExpressions has no "endpoint without the label passes anyway" exception. A
357+ // role-scoped InferencePool selector makes decode-filter's allowsNoLabel safe,
358+ // since an unlabeled pod is never a candidate in the first place; a shared
359+ // InferencePool selector removes that guarantee, so the decode role must be
360+ // scoped with label-selector-filter instead to reject unlabeled/mislabeled pods.
361+ func TestLabelSelectorFilterHasNoAllowsNoLabelEscapeHatch (t * testing.T ) {
362+ rawParams := json .RawMessage (`{"matchExpressions": [{"key": "llm-d.ai/role", "operator": "In", "values": ["decode", "prefill-decode"]}]}` )
363+ plugin , err := bylabel .SelectorFactory ("decode-filter" , fwkplugin .StrictDecoder (rawParams ), nil )
364+ require .NoError (t , err )
365+ blf , ok := plugin .(* bylabel.Selector )
366+ require .True (t , ok )
367+
368+ ctx := utils .NewTestContext (t )
369+ decodeEndpoint := createEndpoint (k8stypes.NamespacedName {Name : "decode-0" }, "10.0.0.1" , map [string ]string {"llm-d.ai/role" : "decode" })
370+ encodeEndpoint := createEndpoint (k8stypes.NamespacedName {Name : "encode-0" }, "10.0.0.2" , map [string ]string {"llm-d.ai/role" : "encode" })
371+ unlabeledEndpoint := createEndpoint (k8stypes.NamespacedName {Name : "mystery-0" }, "10.0.0.3" , map [string ]string {})
372+
373+ result := blf .Filter (ctx , nil , []scheduling.Endpoint {decodeEndpoint , encodeEndpoint , unlabeledEndpoint })
374+ assert .Equal (t , []scheduling.Endpoint {decodeEndpoint }, result ,
375+ "only the decode-labeled endpoint should pass; the mislabeled and unlabeled endpoints must not" )
376+ }
377+
352378// Example for setting Prefill/Decode roles using a LabelSelector filter.
353379// Definition of labels is based on https://github.com/llm-d/llm-d-router/issues/220.
354380func ExamplePrefillDecodeRolesInLWS () {
0 commit comments