@@ -429,6 +429,115 @@ func TestCreateMissingDataProducers(t *testing.T) {
429429 }
430430}
431431
432+ // mockMayConsumerPlugin is a plugin that optionally consumes certain data keys.
433+ type mockMayConsumerPlugin struct {
434+ name string
435+ mayConsume map [fwkplugin.DataKey ]any
436+ }
437+
438+ func (m * mockMayConsumerPlugin ) TypedName () fwkplugin.TypedName {
439+ return fwkplugin.TypedName {Name : m .name , Type : "mock" }
440+ }
441+
442+ func (m * mockMayConsumerPlugin ) MayConsume () map [fwkplugin.DataKey ]any {
443+ return m .mayConsume
444+ }
445+
446+ // mockMixedConsumerPlugin is a plugin that has both required Consumes and optional MayConsume.
447+ // This models a real plugin like prefix cache scorer — requires prefix-match data,
448+ // but optionally uses tokenized input and falls back to raw text if unavailable.
449+ type mockMixedConsumerPlugin struct {
450+ name string
451+ consumes map [fwkplugin.DataKey ]any
452+ mayConsume map [fwkplugin.DataKey ]any
453+ }
454+
455+ func (m * mockMixedConsumerPlugin ) TypedName () fwkplugin.TypedName {
456+ return fwkplugin.TypedName {Name : m .name , Type : "mock" }
457+ }
458+
459+ func (m * mockMixedConsumerPlugin ) Consumes () map [fwkplugin.DataKey ]any {
460+ return m .consumes
461+ }
462+
463+ func (m * mockMixedConsumerPlugin ) MayConsume () map [fwkplugin.DataKey ]any {
464+ return m .mayConsume
465+ }
466+
467+ func TestCreateMissingDataProducers_MayConsume (t * testing.T ) {
468+ producerTypeA := "producer-a"
469+ keyA := fwkplugin .NewDataKey ("keyA" , producerTypeA )
470+
471+ producerAFactory := fwkplugin .FactoryFunc (func (name string , _ json.RawMessage , handle fwkplugin.Handle ) (fwkplugin.Plugin , error ) {
472+ return & mockDataProducerP {name : name , produces : map [fwkplugin.DataKey ]any {keyA : nil }}, nil
473+ })
474+
475+ testCases := []struct {
476+ name string
477+ existingPlugins []fwkplugin.Plugin
478+ factoryRegistry map [string ]fwkplugin.FactoryFunc
479+ wantErr bool
480+ }{
481+ {
482+ name : "MayConsume key with no producer — warning only, no error" ,
483+ existingPlugins : []fwkplugin.Plugin {
484+ & mockMayConsumerPlugin {
485+ name : "optional-consumer" ,
486+ mayConsume : map [fwkplugin.DataKey ]any {keyA : nil },
487+ },
488+ },
489+ factoryRegistry : map [string ]fwkplugin.FactoryFunc {},
490+ wantErr : false , // must NOT error
491+ },
492+ {
493+ name : "MayConsume key with a producer present — no warning, no error" ,
494+ existingPlugins : []fwkplugin.Plugin {
495+ & mockDataProducerP {name : "producer" , produces : map [fwkplugin.DataKey ]any {keyA : nil }},
496+ & mockMayConsumerPlugin {
497+ name : "optional-consumer" ,
498+ mayConsume : map [fwkplugin.DataKey ]any {keyA : nil },
499+ },
500+ },
501+ factoryRegistry : map [string ]fwkplugin.FactoryFunc {producerTypeA : producerAFactory },
502+ wantErr : false ,
503+ },
504+ {
505+ // Models the real prefix cache scorer — it requires prefix-match data (Consumes)
506+ // but optionally uses tokenized input (MayConsume), falling back to raw text.
507+ // The required key has a producer. The optional key does not.
508+ // Result: no error. Warning logged for the missing optional key.
509+ name : "plugin with both Consumes and MayConsume — required key has producer, optional does not" ,
510+ existingPlugins : []fwkplugin.Plugin {
511+ & mockDataProducerP {name : "required-producer" , produces : map [fwkplugin.DataKey ]any {keyA : nil }},
512+ & mockMixedConsumerPlugin {
513+ name : "prefix-cache-scorer" ,
514+ consumes : map [fwkplugin.DataKey ]any {keyA : nil },
515+ mayConsume : map [fwkplugin.DataKey ]any {fwkplugin .NewDataKey ("tokenized-input" , "tokenizer" ): nil },
516+ },
517+ },
518+ factoryRegistry : map [string ]fwkplugin.FactoryFunc {},
519+ wantErr : false ,
520+ },
521+ }
522+
523+ for _ , tc := range testCases {
524+ t .Run (tc .name , func (t * testing.T ) {
525+ handle := fwkplugin .NewEppHandle (context .Background (), func () []k8stypes.NamespacedName { return nil })
526+ for _ , p := range tc .existingPlugins {
527+ handle .AddPlugin (p .TypedName ().Name , p )
528+ }
529+
530+ err := CreateMissingDataProducers (context .Background (), map [string ]string {}, tc .factoryRegistry , handle )
531+
532+ if tc .wantErr {
533+ assert .Error (t , err )
534+ return
535+ }
536+ assert .NoError (t , err )
537+ })
538+ }
539+ }
540+
432541func assertTopologicalOrder (t * testing.T , dag map [string ][]string , ordered []string ) {
433542 t .Helper ()
434543 positions := make (map [string ]int )
0 commit comments