feat: replace tui action stubs
This commit is contained in:
parent
7e0a00ff43
commit
86e5bb5f0c
76
cmd/extra.go
76
cmd/extra.go
|
|
@ -13,31 +13,12 @@ var importCmd = &cobra.Command{
|
||||||
Use: "import",
|
Use: "import",
|
||||||
Short: "Import servers from ~/.ssh/config",
|
Short: "Import servers from ~/.ssh/config",
|
||||||
RunE: func(cmd *cobra.Command, args []string) error {
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
servers, err := ssh.ImportFromSSHConfig()
|
imported, err := importServersFromSSHConfig(func(format string, args ...interface{}) {
|
||||||
|
fmt.Printf(format+"\n", args...)
|
||||||
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("import: %w", err)
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(servers) == 0 {
|
|
||||||
fmt.Println("No servers found in ~/.ssh/config")
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
imported := 0
|
|
||||||
for _, s := range servers {
|
|
||||||
existing, _ := appDB.GetServer(s.Alias)
|
|
||||||
if existing != nil {
|
|
||||||
fmt.Printf(" skip (exists): %s\n", s.Alias)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if err := appDB.CreateServer(s); err != nil {
|
|
||||||
fmt.Printf(" error: %s: %v\n", s.Alias, err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
fmt.Printf(" imported: %s (%s@%s:%d)\n", s.Alias, s.User, s.Host, s.Port)
|
|
||||||
imported++
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Printf("\nImported %d servers.\n", imported)
|
fmt.Printf("\nImported %d servers.\n", imported)
|
||||||
return nil
|
return nil
|
||||||
},
|
},
|
||||||
|
|
@ -52,13 +33,56 @@ var exportCmd = &cobra.Command{
|
||||||
return fmt.Errorf("list servers: %w", err)
|
return fmt.Errorf("list servers: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, s := range servers {
|
fmt.Print(formatServersExport(servers))
|
||||||
fmt.Printf("%s\t%s@%s:%d\t%s\n", s.Alias, s.User, s.Host, s.Port, s.AuthMethod)
|
|
||||||
}
|
|
||||||
return nil
|
return nil
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func importServersFromSSHConfig(report func(format string, args ...interface{})) (int, error) {
|
||||||
|
servers, err := ssh.ImportFromSSHConfig()
|
||||||
|
if err != nil {
|
||||||
|
return 0, fmt.Errorf("import: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(servers) == 0 {
|
||||||
|
if report != nil {
|
||||||
|
report("No servers found in ~/.ssh/config")
|
||||||
|
}
|
||||||
|
return 0, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
imported := 0
|
||||||
|
for _, s := range servers {
|
||||||
|
existing, _ := appDB.GetServer(s.Alias)
|
||||||
|
if existing != nil {
|
||||||
|
if report != nil {
|
||||||
|
report(" skip (exists): %s", s.Alias)
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if err := appDB.CreateServer(s); err != nil {
|
||||||
|
if report != nil {
|
||||||
|
report(" error: %s: %v", s.Alias, err)
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if report != nil {
|
||||||
|
report(" imported: %s (%s@%s:%d)", s.Alias, s.User, s.Host, s.Port)
|
||||||
|
}
|
||||||
|
imported++
|
||||||
|
}
|
||||||
|
|
||||||
|
return imported, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func formatServersExport(servers []*model.Server) string {
|
||||||
|
var b strings.Builder
|
||||||
|
for _, s := range servers {
|
||||||
|
fmt.Fprintf(&b, "%s\t%s@%s:%d\t%s\n", s.Alias, s.User, s.Host, s.Port, s.AuthMethod)
|
||||||
|
}
|
||||||
|
return b.String()
|
||||||
|
}
|
||||||
|
|
||||||
var runCmd = &cobra.Command{
|
var runCmd = &cobra.Command{
|
||||||
Use: "run <alias> <command>",
|
Use: "run <alias> <command>",
|
||||||
Short: "Run a command on a server",
|
Short: "Run a command on a server",
|
||||||
|
|
|
||||||
37
cmd/tui.go
37
cmd/tui.go
|
|
@ -138,6 +138,14 @@ func runTUI() error {
|
||||||
tui.DeleteForward = func(forwardID int64) error {
|
tui.DeleteForward = func(forwardID int64) error {
|
||||||
return appDB.DeleteForward(forwardID)
|
return appDB.DeleteForward(forwardID)
|
||||||
}
|
}
|
||||||
|
tui.ImportServers = func() (int, error) {
|
||||||
|
return importServersFromSSHConfig(nil)
|
||||||
|
}
|
||||||
|
tui.LockVault = func() error {
|
||||||
|
v := getOrCreateVault()
|
||||||
|
v.Lock()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
tui.UpdateTestResult = func(alias string, status model.TestStatus, testErr string) error {
|
tui.UpdateTestResult = func(alias string, status model.TestStatus, testErr string) error {
|
||||||
return appDB.UpdateTestResult(alias, status, testErr)
|
return appDB.UpdateTestResult(alias, status, testErr)
|
||||||
}
|
}
|
||||||
|
|
@ -213,6 +221,35 @@ func runTUI() error {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if result != nil && result.Action == "export" {
|
||||||
|
servers, err := appDB.ListServers()
|
||||||
|
if err != nil {
|
||||||
|
fmt.Fprintf(os.Stderr, "Export error: %v\n", err)
|
||||||
|
} else {
|
||||||
|
fmt.Print(formatServersExport(servers))
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println("\n[Press Enter to return to sshkeeper]")
|
||||||
|
buf := make([]byte, 1)
|
||||||
|
os.Stdin.Read(buf)
|
||||||
|
|
||||||
|
servers, _ = appDB.ListServers()
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if result != nil && result.Action == "vault_change_pw" {
|
||||||
|
if err := changeVaultPasswordInteractive(); err != nil {
|
||||||
|
fmt.Fprintf(os.Stderr, "Vault password change error: %v\n", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println("\n[Press Enter to return to sshkeeper]")
|
||||||
|
buf := make([]byte, 1)
|
||||||
|
os.Stdin.Read(buf)
|
||||||
|
|
||||||
|
servers, _ = appDB.ListServers()
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
if result != nil && (result.Action == "tunnel" || result.Action == "tunnel_n" || result.Action == "tunnel_bg") && result.Server != nil {
|
if result != nil && (result.Action == "tunnel" || result.Action == "tunnel_n" || result.Action == "tunnel_bg") && result.Server != nil {
|
||||||
server := result.Server
|
server := result.Server
|
||||||
fresh, err := appDB.GetServer(server.Alias)
|
fresh, err := appDB.GetServer(server.Alias)
|
||||||
|
|
|
||||||
72
cmd/vault.go
72
cmd/vault.go
|
|
@ -118,40 +118,7 @@ var vaultChangePasswordCmd = &cobra.Command{
|
||||||
Use: "change-password",
|
Use: "change-password",
|
||||||
Short: "Change master password",
|
Short: "Change master password",
|
||||||
RunE: func(cmd *cobra.Command, args []string) error {
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
v := getOrCreateVault()
|
return changeVaultPasswordInteractive()
|
||||||
|
|
||||||
if err := unlockVaultForCommand(v); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Print("New master password: ")
|
|
||||||
pw1, err := term.ReadPassword(int(syscall.Stdin))
|
|
||||||
fmt.Println()
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("read password: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(pw1) == 0 {
|
|
||||||
return fmt.Errorf("password cannot be empty")
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Print("Repeat new master password: ")
|
|
||||||
pw2, err := term.ReadPassword(int(syscall.Stdin))
|
|
||||||
fmt.Println()
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("read password: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if string(pw1) != string(pw2) {
|
|
||||||
return fmt.Errorf("passwords do not match")
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := v.ChangePassword(string(pw1)); err != nil {
|
|
||||||
return fmt.Errorf("change password: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Println("Master password changed.")
|
|
||||||
return nil
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -220,6 +187,43 @@ func unlockVaultForCommand(v *vault.Vault) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func changeVaultPasswordInteractive() error {
|
||||||
|
v := getOrCreateVault()
|
||||||
|
|
||||||
|
if err := unlockVaultForCommand(v); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Print("New master password: ")
|
||||||
|
pw1, err := term.ReadPassword(int(syscall.Stdin))
|
||||||
|
fmt.Println()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("read password: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(pw1) == 0 {
|
||||||
|
return fmt.Errorf("password cannot be empty")
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Print("Repeat new master password: ")
|
||||||
|
pw2, err := term.ReadPassword(int(syscall.Stdin))
|
||||||
|
fmt.Println()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("read password: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if string(pw1) != string(pw2) {
|
||||||
|
return fmt.Errorf("passwords do not match")
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := v.ChangePassword(string(pw1)); err != nil {
|
||||||
|
return fmt.Errorf("change password: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println("Master password changed.")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func vaultLockedProcessMessage() string {
|
func vaultLockedProcessMessage() string {
|
||||||
return "vault is locked in this process; enter the master password when this command prompts for it"
|
return "vault is locked in this process; enter the master password when this command prompts for it"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -94,6 +94,12 @@ type forwardDeletedMsg struct {
|
||||||
err error
|
err error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type importDoneMsg struct {
|
||||||
|
servers []*model.Server
|
||||||
|
count int
|
||||||
|
err error
|
||||||
|
}
|
||||||
|
|
||||||
// --- List items ---
|
// --- List items ---
|
||||||
|
|
||||||
type serverItem struct {
|
type serverItem struct {
|
||||||
|
|
@ -171,6 +177,8 @@ var (
|
||||||
SaveForward func(fwd *model.Forward) error
|
SaveForward func(fwd *model.Forward) error
|
||||||
UpdateForward func(fwd *model.Forward) error
|
UpdateForward func(fwd *model.Forward) error
|
||||||
DeleteForward func(forwardID int64) error
|
DeleteForward func(forwardID int64) error
|
||||||
|
ImportServers func() (int, error)
|
||||||
|
LockVault func() error
|
||||||
)
|
)
|
||||||
|
|
||||||
// --- Screen type ---
|
// --- Screen type ---
|
||||||
|
|
@ -240,6 +248,7 @@ type tuiModel struct {
|
||||||
confirmAction func() tea.Cmd
|
confirmAction func() tea.Cmd
|
||||||
fullHelp *fullHelpModel
|
fullHelp *fullHelpModel
|
||||||
}
|
}
|
||||||
|
|
||||||
func New(servers []*model.Server) *tuiModel {
|
func New(servers []*model.Server) *tuiModel {
|
||||||
items := make([]list.Item, len(servers))
|
items := make([]list.Item, len(servers))
|
||||||
for i, s := range servers {
|
for i, s := range servers {
|
||||||
|
|
@ -354,6 +363,20 @@ func (m *tuiModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||||
m.pendingTemplate = nil
|
m.pendingTemplate = nil
|
||||||
return m, nil
|
return m, nil
|
||||||
|
|
||||||
|
case importDoneMsg:
|
||||||
|
if msg.err != nil {
|
||||||
|
m.err = msg.err
|
||||||
|
return m, nil
|
||||||
|
}
|
||||||
|
m.servers = msg.servers
|
||||||
|
items := make([]list.Item, len(msg.servers))
|
||||||
|
for i, s := range msg.servers {
|
||||||
|
items[i] = serverItem{server: s}
|
||||||
|
}
|
||||||
|
m.list.SetItems(items)
|
||||||
|
m.success = fmt.Sprintf("Imported %d server(s).", msg.count)
|
||||||
|
return m, nil
|
||||||
|
|
||||||
case forwardsLoadedMsg:
|
case forwardsLoadedMsg:
|
||||||
if m.forwardScreen != nil {
|
if m.forwardScreen != nil {
|
||||||
if msg.err != nil {
|
if msg.err != nil {
|
||||||
|
|
@ -1093,9 +1116,13 @@ func (m *tuiModel) updateActionMenu(msg tea.KeyMsg) (tea.Model, tea.Cmd) {
|
||||||
m.actionMenu = nil
|
m.actionMenu = nil
|
||||||
return m, m.tunnelScreen.loadTunnels()
|
return m, m.tunnelScreen.loadTunnels()
|
||||||
case "route":
|
case "route":
|
||||||
m.err = fmt.Errorf("route management not yet implemented in TUI")
|
if item, ok := m.list.SelectedItem().(serverItem); ok {
|
||||||
m.screen = screenList
|
m.form = newEditFormModel(item.server, m.width, m.height)
|
||||||
|
m.form.focusIdx = 7
|
||||||
|
m.form.updateFocus()
|
||||||
|
m.screen = screenForm
|
||||||
m.actionMenu = nil
|
m.actionMenu = nil
|
||||||
|
}
|
||||||
case "test":
|
case "test":
|
||||||
if item, ok := m.list.SelectedItem().(serverItem); ok {
|
if item, ok := m.list.SelectedItem().(serverItem); ok {
|
||||||
m.screen = screenList
|
m.screen = screenList
|
||||||
|
|
@ -1128,19 +1155,35 @@ func (m *tuiModel) updateActionMenu(msg tea.KeyMsg) (tea.Model, tea.Cmd) {
|
||||||
case "import":
|
case "import":
|
||||||
m.screen = screenList
|
m.screen = screenList
|
||||||
m.actionMenu = nil
|
m.actionMenu = nil
|
||||||
m.err = fmt.Errorf("import not yet implemented")
|
return m, func() tea.Msg {
|
||||||
|
if ImportServers == nil {
|
||||||
|
return importDoneMsg{err: fmt.Errorf("import is unavailable")}
|
||||||
|
}
|
||||||
|
count, err := ImportServers()
|
||||||
|
if err != nil {
|
||||||
|
return importDoneMsg{err: err}
|
||||||
|
}
|
||||||
|
servers, err := ListServers()
|
||||||
|
return importDoneMsg{servers: servers, count: count, err: err}
|
||||||
|
}
|
||||||
case "export":
|
case "export":
|
||||||
m.screen = screenList
|
|
||||||
m.actionMenu = nil
|
m.actionMenu = nil
|
||||||
m.err = fmt.Errorf("export not yet implemented")
|
m.result = &TUIResult{Action: "export"}
|
||||||
|
return m, tea.Quit
|
||||||
case "vault_lock":
|
case "vault_lock":
|
||||||
m.screen = screenList
|
m.screen = screenList
|
||||||
m.actionMenu = nil
|
m.actionMenu = nil
|
||||||
m.err = fmt.Errorf("vault lock not yet implemented")
|
if LockVault == nil {
|
||||||
|
m.err = fmt.Errorf("vault lock is unavailable")
|
||||||
|
} else if err := LockVault(); err != nil {
|
||||||
|
m.err = err
|
||||||
|
} else {
|
||||||
|
m.success = "Vault locked."
|
||||||
|
}
|
||||||
case "vault_change_pw":
|
case "vault_change_pw":
|
||||||
m.screen = screenList
|
|
||||||
m.actionMenu = nil
|
m.actionMenu = nil
|
||||||
m.err = fmt.Errorf("vault change password not yet implemented")
|
m.result = &TUIResult{Action: "vault_change_pw"}
|
||||||
|
return m, tea.Quit
|
||||||
}
|
}
|
||||||
return m, nil
|
return m, nil
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -819,3 +819,138 @@ func TestActionMenuClosesOnAllActions(t *testing.T) {
|
||||||
t.Fatalf("expected screenForwardList, got %v", m.screen)
|
t.Fatalf("expected screenForwardList, got %v", m.screen)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestActionMenuManageRouteOpensRouteField(t *testing.T) {
|
||||||
|
server := &model.Server{ID: 1, Alias: "web", Host: "web.example.org", Port: 22, User: "root", AuthMethod: model.AuthKey}
|
||||||
|
m := New([]*model.Server{server})
|
||||||
|
m.width = 100
|
||||||
|
m.height = 30
|
||||||
|
m.actionMenu = newActionMenuModel(m.width, m.height)
|
||||||
|
m.screen = screenActionMenu
|
||||||
|
for i := 0; i < len(m.actionMenu.list.Items()); i++ {
|
||||||
|
m.actionMenu.list.Select(i)
|
||||||
|
if item, ok := m.actionMenu.list.SelectedItem().(actionMenuItem); ok && item.action == "route" {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
updated, _ := m.updateActionMenu(tea.KeyMsg{Type: tea.KeyEnter})
|
||||||
|
m = updated.(*tuiModel)
|
||||||
|
|
||||||
|
if m.err != nil && strings.Contains(m.err.Error(), "not yet implemented") {
|
||||||
|
t.Fatalf("route action should not be a stub: %v", m.err)
|
||||||
|
}
|
||||||
|
if m.screen != screenForm || m.form == nil {
|
||||||
|
t.Fatalf("expected route action to open edit form, screen=%v form=%v", m.screen, m.form)
|
||||||
|
}
|
||||||
|
if m.form.focusIdx != 7 {
|
||||||
|
t.Fatalf("expected route field focus index 7, got %d", m.form.focusIdx)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestActionMenuImportUsesCallbackAndRefreshesList(t *testing.T) {
|
||||||
|
server := &model.Server{ID: 1, Alias: "web", Host: "web.example.org", Port: 22, User: "root", AuthMethod: model.AuthKey}
|
||||||
|
imported := false
|
||||||
|
ImportServers = func() (int, error) {
|
||||||
|
imported = true
|
||||||
|
return 2, nil
|
||||||
|
}
|
||||||
|
ListServers = func() ([]*model.Server, error) {
|
||||||
|
return []*model.Server{server}, nil
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
ImportServers = nil
|
||||||
|
ListServers = nil
|
||||||
|
}()
|
||||||
|
|
||||||
|
m := New([]*model.Server{})
|
||||||
|
m.width = 100
|
||||||
|
m.height = 30
|
||||||
|
m.actionMenu = newActionMenuModel(m.width, m.height)
|
||||||
|
m.screen = screenActionMenu
|
||||||
|
for i := 0; i < len(m.actionMenu.list.Items()); i++ {
|
||||||
|
m.actionMenu.list.Select(i)
|
||||||
|
if item, ok := m.actionMenu.list.SelectedItem().(actionMenuItem); ok && item.action == "import" {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
updated, cmd := m.updateActionMenu(tea.KeyMsg{Type: tea.KeyEnter})
|
||||||
|
m = updated.(*tuiModel)
|
||||||
|
if cmd == nil {
|
||||||
|
t.Fatal("expected import command")
|
||||||
|
}
|
||||||
|
msg := cmd()
|
||||||
|
updated, _ = m.Update(msg)
|
||||||
|
m = updated.(*tuiModel)
|
||||||
|
|
||||||
|
if !imported {
|
||||||
|
t.Fatal("expected import callback to run")
|
||||||
|
}
|
||||||
|
if len(m.servers) != 1 || m.servers[0].Alias != "web" {
|
||||||
|
t.Fatalf("expected refreshed server list, got %#v", m.servers)
|
||||||
|
}
|
||||||
|
if !strings.Contains(m.success, "Imported 2") {
|
||||||
|
t.Fatalf("expected import success message, got %q", m.success)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestActionMenuExportAndVaultChangePasswordExitTUI(t *testing.T) {
|
||||||
|
server := &model.Server{ID: 1, Alias: "web", Host: "web.example.org", Port: 22, User: "root", AuthMethod: model.AuthKey}
|
||||||
|
for _, action := range []string{"export", "vault_change_pw"} {
|
||||||
|
t.Run(action, func(t *testing.T) {
|
||||||
|
m := New([]*model.Server{server})
|
||||||
|
m.width = 100
|
||||||
|
m.height = 30
|
||||||
|
m.actionMenu = newActionMenuModel(m.width, m.height)
|
||||||
|
m.screen = screenActionMenu
|
||||||
|
for i := 0; i < len(m.actionMenu.list.Items()); i++ {
|
||||||
|
m.actionMenu.list.Select(i)
|
||||||
|
if item, ok := m.actionMenu.list.SelectedItem().(actionMenuItem); ok && item.action == action {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
updated, cmd := m.updateActionMenu(tea.KeyMsg{Type: tea.KeyEnter})
|
||||||
|
m = updated.(*tuiModel)
|
||||||
|
if cmd == nil {
|
||||||
|
t.Fatalf("expected %s to quit TUI", action)
|
||||||
|
}
|
||||||
|
if m.result == nil || m.result.Action != action {
|
||||||
|
t.Fatalf("expected result action %q, got %#v", action, m.result)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestActionMenuVaultLockUsesCallback(t *testing.T) {
|
||||||
|
server := &model.Server{ID: 1, Alias: "web", Host: "web.example.org", Port: 22, User: "root", AuthMethod: model.AuthKey}
|
||||||
|
locked := false
|
||||||
|
LockVault = func() error {
|
||||||
|
locked = true
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
defer func() { LockVault = nil }()
|
||||||
|
|
||||||
|
m := New([]*model.Server{server})
|
||||||
|
m.width = 100
|
||||||
|
m.height = 30
|
||||||
|
m.actionMenu = newActionMenuModel(m.width, m.height)
|
||||||
|
m.screen = screenActionMenu
|
||||||
|
for i := 0; i < len(m.actionMenu.list.Items()); i++ {
|
||||||
|
m.actionMenu.list.Select(i)
|
||||||
|
if item, ok := m.actionMenu.list.SelectedItem().(actionMenuItem); ok && item.action == "vault_lock" {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
updated, _ := m.updateActionMenu(tea.KeyMsg{Type: tea.KeyEnter})
|
||||||
|
m = updated.(*tuiModel)
|
||||||
|
|
||||||
|
if !locked {
|
||||||
|
t.Fatal("expected vault lock callback to run")
|
||||||
|
}
|
||||||
|
if !strings.Contains(m.success, "Vault locked") {
|
||||||
|
t.Fatalf("expected vault lock success, got %q", m.success)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue