okami.mom/albedo
git clone https://git.nkp.pet/okami.mom/albedo
132f576
main
1// Login's lists and removal question are keystroke behaviour the e2e suite 2// reaches only through the daemon's flows: a letter typed into the always-live 3// search must not remove a provider (the old bare "d" did), removal waits for 4// enter, and a text step takes letters as input rather than as actions. 5package tui 6 7import ( 8"albedo/cli/internal/daemon" 9"strings" 10"testing" 11 12"github.com/charmbracelet/x/ansi" 13 14 tea"charm.land/bubbletea/v2" 15) 16 17func TestLoginRemovalAsksThenTakesEnterOnly (t * testing.T ) { 18m := loginModel (120 ,30 ) 19m ,_ = m .Update (tea.KeyPressMsg {Code :'d' ,Mod :tea .ModCtrl }) 20if m .Step != StepRemove || ! m .confirm .asking () { 21t .Fatalf ("ctrl+d did not ask about the highlighted provider (step %v):\n%s" ,m .Step ,ansi .Strip (m .View ())) 22 } 23m ,cmd := m .Update (tea.KeyPressMsg {Code :'x' ,Text :"x" }) 24if cmd != nil || m .Step != StepRemove { 25t .Fatal ("a stray key changed the removal question" ) 26 } 27m ,_ = m .Update (tea.KeyPressMsg {Code :tea .KeyEscape }) 28if m .Step != StepChoose || m .confirm .asking () { 29t .Fatalf ("esc did not keep the provider (step %v)" ,m .Step ) 30 } 31m ,_ = m .Update (tea.KeyPressMsg {Code :'d' ,Mod :tea .ModCtrl }) 32m ,cmd = m .Update (tea.KeyPressMsg {Code :tea .KeyEnter }) 33if m .Step != StepSaving || cmd == nil { 34t .Fatalf ("enter did not remove (step %v)" ,m .Step ) 35 } 36} 37 38func TestLoginLetterInTheSearchFiltersInsteadOfRemoving (t * testing.T ) { 39m := loginModel (120 ,30 ) 40m ,_ = m .Update (tea.KeyPressMsg {Code :'d' ,Text :"d" }) 41if m .Step != StepChoose || m .confirm .asking () { 42t .Fatalf ("a bare letter started a removal (step %v)" ,m .Step ) 43 } 44if m .ChoosePicker .input .Value ()!= "d" { 45t .Fatalf ("the letter did not reach the search: %q" ,m .ChoosePicker .input .Value ()) 46 } 47} 48 49func TestLoginFilterFuzzilyFindsAProvider (t * testing.T ) { 50m := loginModel (120 ,30 ) 51for _ ,r := range "cla" { 52m ,_ = m .Update (tea.KeyPressMsg {Code :r ,Text :string (r )}) 53 } 54view := ansi .Strip (m .View ()) 55if ! strings .Contains (view ,"claude-team" )|| strings .Contains (view ,"Antigravity" ) { 56t .Fatalf ("the search did not narrow to the claude providers:\n%s" ,view ) 57 } 58if item ,ok := m .ChoosePicker .Highlighted ();! ok || ! strings .HasPrefix (item .ID ,"use:claude" )&& ! strings .HasPrefix (item .ID ,"signin:claude" ) { 59t .Fatalf ("the best match is not a claude row: %+v" ,item ) 60 } 61} 62 63func TestLoginDetailShowsWithoutEnter (t * testing.T ) { 64m := loginModel (120 ,30 ) 65view := ansi .Strip (m .View ()) 66if ! strings .Contains (view ,"a saved provider profile" ) { 67t .Fatalf ("the highlighted provider's detail is not in the pane:\n%s" ,view ) 68 } 69} 70 71func TestLoginKeyEntryTakesLettersAsInput (t * testing.T ) { 72m := loginModel (120 ,30 ) 73m .askAPIKey () 74for _ ,r := range "dx" { 75m ,_ = m .Update (tea.KeyPressMsg {Code :r ,Text :string (r )}) 76 } 77if m .TextInput .Value ()!= "dx" || m .Step != StepAPIKey { 78t .Fatalf ("typing into the key step went elsewhere: %q on step %v" ,m .TextInput .Value (),m .Step ) 79 } 80} 81func TestServiceSignInCompletionSkipsModelsAndReturnsToChoose (t * testing.T ) { 82m := loginModel (120 ,30 ) 83serviceLogin := daemon.SignIn {Provider :"colab" ,Label :"Google Colab" ,Kind :"service" ,Flows : []string {"browser" }} 84m .SignIns = append (m .SignIns ,serviceLogin ) 85m .startSignIn (serviceLogin ) 86 87serviceAcc := daemon.Account {ID :"acc-colab" ,Provider :"colab" ,Label :"colab-user@example.com" ,Kind :"service" } 88m ,_ = m .Update (signInStatusMsg { 89State :"complete" , 90Accounts : []daemon.Account {serviceAcc }, 91Gen :m .Generation , 92 }) 93 94if m .Step != StepChoose { 95t .Fatalf ("service login completion did not return to StepChoose, got: %v" ,m .Step ) 96 } 97if ! strings .Contains (m .Status ,"Signed in to Google Colab" ) { 98t .Fatalf ("unexpected status after service login completion: %q" ,m .Status ) 99 } 100if m .Error != "" { 101t .Fatalf ("unexpected error after service login completion: %q" ,m .Error ) 102 } 103found := false 104for _ ,acc := range m .Accounts { 105if acc .ID == "acc-colab" { 106found = true 107break 108 } 109 } 110if ! found { 111t .Fatal ("new service account was not stored in model accounts" ) 112 } 113} 114 115func TestServiceAccountSelectionDoesNotDraftModelProfile (t * testing.T ) { 116m := loginModel (120 ,30 ) 117serviceAcc := daemon.Account {ID :"acc-colab" ,Provider :"colab" ,Label :"colab-user@example.com" ,Kind :"service" } 118m .Accounts = append (m .Accounts ,serviceAcc ) 119m .buildChoosePicker () 120 121cmd := m .chooseAccount (serviceAcc ) 122if cmd != nil { 123t .Fatalf ("expected nil cmd for service account choose, got %v" ,cmd ) 124 } 125if m .Step != StepChoose { 126t .Fatalf ("expected to remain on StepChoose, got %v" ,m .Step ) 127 } 128if ! strings .Contains (m .Status ,"ready for use with tools" ) { 129t .Fatalf ("unexpected status: %q" ,m .Status ) 130 } 131} 132 133func TestServiceAccountAndSignInNotesInChoosePicker (t * testing.T ) { 134m := loginModel (120 ,30 ) 135m .SignIns = append (m .SignIns , daemon.SignIn {Provider :"colab" ,Label :"Google Colab" ,Kind :"service" }) 136m .Accounts = append (m .Accounts , daemon.Account {ID :"acc-colab" ,Provider :"colab" ,Label :"colab-user@example.com" ,Kind :"service" }) 137m .buildChoosePicker () 138 139var accountItem ,signInItem * PickerItem 140for i := range m .ChoosePicker .items { 141item := & m .ChoosePicker .items [i ] 142if strings .HasPrefix (item .ID ,"account:" )&& item .Label == "colab-user@example.com" { 143accountItem = item 144 } 145if item .ID == "signin:colab" { 146signInItem = item 147 } 148 } 149if accountItem == nil || accountItem .Note != "service account for tools; its tokens stay in creds.json" { 150t .Fatalf ("unexpected service account note: %+v" ,accountItem ) 151 } 152if signInItem == nil || signInItem .Note != "service sign-in for tools; its tokens stay in creds.json" { 153t .Fatalf ("unexpected service sign-in note: %+v" ,signInItem ) 154 } 155}