@@ -498,6 +498,208 @@ func TestGWSCallbackAndNgrok(t *testing.T) {
498498 }
499499}
500500
501+ func TestProvidersBeforeModels (t * testing.T ) {
502+ cfg := config .Default ()
503+ svc := testService (cfg )
504+
505+ modelsNode := svc .Tree ()[1 ]
506+ if modelsNode .Category == nil || modelsNode .Category .Label != "Models" {
507+ t .Fatal ("expected Models category at index 1" )
508+ }
509+
510+ first := modelsNode .Category .Children [0 ]
511+ if first .Category == nil || first .Category .Key != "models.providers" {
512+ t .Errorf ("first child of Models should be Providers, got %+v" , first )
513+ }
514+ }
515+
516+ func TestModelTierFieldsAreSelects (t * testing.T ) {
517+ cfg := config .Default ()
518+ svc := testService (cfg )
519+
520+ for _ , key := range []string {"models.default" , "models.complex" , "models.fast" , "models.nano" } {
521+ f := findField (svc , key )
522+ if f == nil {
523+ t .Fatalf ("%s field not found" , key )
524+ }
525+ if f .Type != TypeSelect {
526+ t .Errorf ("%s type = %d, want TypeSelect (%d)" , key , f .Type , TypeSelect )
527+ }
528+ if f .OptionsFunc == nil {
529+ t .Errorf ("%s should have OptionsFunc" , key )
530+ }
531+ }
532+ }
533+
534+ func TestModelOptionsShowOnlyConfiguredProviders (t * testing.T ) {
535+ cfg := & config.Config {
536+ Models : & config.ModelsConfig {
537+ Providers : map [string ]config.ModelProviderConfig {
538+ "anthropic" : {APIKeyRef : "keychain:obk/anthropic" },
539+ },
540+ },
541+ }
542+ svc := testService (cfg )
543+
544+ field := findField (svc , "models.default" )
545+ if field == nil {
546+ t .Fatal ("models.default not found" )
547+ }
548+
549+ opts := svc .ResolvedOptions (field )
550+ for _ , o := range opts {
551+ if o .Value == "" {
552+ continue
553+ }
554+ if ! strings .HasPrefix (o .Value , "anthropic/" ) {
555+ t .Errorf ("option %q should be from anthropic, got %q" , o .Label , o .Value )
556+ }
557+ }
558+ if len (opts ) < 2 {
559+ t .Errorf ("expected at least (none) + 1 anthropic model, got %d options" , len (opts ))
560+ }
561+ }
562+
563+ func TestModelOptionsNoProvidersConfigured (t * testing.T ) {
564+ cfg := & config.Config {}
565+ svc := testService (cfg )
566+
567+ field := findField (svc , "models.default" )
568+ if field == nil {
569+ t .Fatal ("models.default not found" )
570+ }
571+
572+ opts := svc .ResolvedOptions (field )
573+ if len (opts ) != 1 {
574+ t .Fatalf ("expected 1 placeholder option, got %d" , len (opts ))
575+ }
576+ if opts [0 ].Value != "" {
577+ t .Errorf ("placeholder option value = %q, want empty" , opts [0 ].Value )
578+ }
579+ }
580+
581+ func TestProfileFilteredByConfiguredProviders (t * testing.T ) {
582+ cfg := & config.Config {
583+ Models : & config.ModelsConfig {
584+ Providers : map [string ]config.ModelProviderConfig {
585+ "gemini" : {APIKeyRef : "keychain:obk/gemini" },
586+ },
587+ },
588+ }
589+ svc := testService (cfg )
590+
591+ field := findField (svc , "models.profile" )
592+ if field == nil {
593+ t .Fatal ("models.profile not found" )
594+ }
595+
596+ opts := svc .ResolvedOptions (field )
597+ for _ , o := range opts {
598+ if o .Value == "" {
599+ continue
600+ }
601+ p , ok := config .Profiles [o .Value ]
602+ if ! ok {
603+ continue
604+ }
605+ for _ , req := range p .Providers {
606+ if req != "gemini" {
607+ t .Errorf ("profile %q requires %q but only gemini is configured" , o .Value , req )
608+ }
609+ }
610+ }
611+ }
612+
613+ func TestAfterSetCalledOnAPIKey (t * testing.T ) {
614+ cfg := config .Default ()
615+ verified := false
616+ svc := New (cfg ,
617+ WithSaveFn (func (* config.Config ) error { return nil }),
618+ WithStoreCred (func (ref , value string ) error { return nil }),
619+ WithLoadCred (func (ref string ) (string , error ) { return "key" , nil }),
620+ WithVerifyProvider (func (name string , pcfg config.ModelProviderConfig ) error {
621+ verified = true
622+ return nil
623+ }),
624+ )
625+
626+ field := findField (svc , "models.providers.anthropic.api_key" )
627+ if field == nil {
628+ t .Fatal ("anthropic api_key not found" )
629+ }
630+ if field .AfterSet == nil {
631+ t .Fatal ("api_key field should have AfterSet" )
632+ }
633+
634+ if err := svc .SetValue (field , "sk-test" ); err != nil {
635+ t .Fatal (err )
636+ }
637+
638+ msg := field .AfterSet (svc )
639+ if ! verified {
640+ t .Error ("verifyProvider was not called" )
641+ }
642+ if ! strings .Contains (msg , "verified" ) {
643+ t .Errorf ("AfterSet msg = %q, want to contain 'verified'" , msg )
644+ }
645+ }
646+
647+ func TestAfterSetVerifyFailure (t * testing.T ) {
648+ cfg := config .Default ()
649+ svc := New (cfg ,
650+ WithSaveFn (func (* config.Config ) error { return nil }),
651+ WithStoreCred (func (ref , value string ) error { return nil }),
652+ WithLoadCred (func (ref string ) (string , error ) { return "key" , nil }),
653+ WithVerifyProvider (func (name string , pcfg config.ModelProviderConfig ) error {
654+ return fmt .Errorf ("invalid key" )
655+ }),
656+ )
657+
658+ field := findField (svc , "models.providers.openai.api_key" )
659+ if field == nil {
660+ t .Fatal ("openai api_key not found" )
661+ }
662+
663+ if err := svc .SetValue (field , "sk-bad" ); err != nil {
664+ t .Fatal (err )
665+ }
666+
667+ msg := field .AfterSet (svc )
668+ if ! strings .Contains (msg , "Warning" ) {
669+ t .Errorf ("AfterSet msg = %q, want to contain 'Warning'" , msg )
670+ }
671+ if ! strings .Contains (msg , "invalid key" ) {
672+ t .Errorf ("AfterSet msg = %q, want to contain error detail" , msg )
673+ }
674+ }
675+
676+ func TestResolvedOptionsFallsBackToStatic (t * testing.T ) {
677+ cfg := config .Default ()
678+ svc := testService (cfg )
679+
680+ field := findField (svc , "mode" )
681+ if field == nil {
682+ t .Fatal ("mode field not found" )
683+ }
684+
685+ opts := svc .ResolvedOptions (field )
686+ if len (opts ) != 3 {
687+ t .Errorf ("mode options count = %d, want 3" , len (opts ))
688+ }
689+ }
690+
691+ func TestVerifyProviderNilHandler (t * testing.T ) {
692+ cfg := config .Default ()
693+ svc := New (cfg ,
694+ WithSaveFn (func (* config.Config ) error { return nil }),
695+ )
696+
697+ err := svc .VerifyProvider ("anthropic" , config.ModelProviderConfig {})
698+ if err != nil {
699+ t .Errorf ("nil verifyProvider should return nil, got %v" , err )
700+ }
701+ }
702+
501703func findField (svc * Service , key string ) * Field {
502704 return findFieldInNodes (svc .Tree (), key )
503705}
0 commit comments