Skip to content

Commit 830edfa

Browse files
committed
fix(BRE2-827): "this node" should be first in the list
1 parent f5105a0 commit 830edfa

2 files changed

Lines changed: 108 additions & 1 deletion

File tree

pkg/cmd/register/sshkeys.go

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,13 +34,20 @@ func SelectNodeFromList(ctx context.Context, t *terminal.Terminal, prompter term
3434
thisNodeID = reg.ExternalNodeID
3535
}
3636
t.Vprint("")
37+
thisIdx := -1
3738
labels := make([]string, len(nodes))
3839
for i, n := range nodes {
39-
labels[i] = fmt.Sprintf("%s (%s)", n.GetName(), n.GetExternalNodeId())
4040
if thisNodeID != "" && n.GetExternalNodeId() == thisNodeID {
4141
labels[i] = fmt.Sprintf("%s (%s) — this node", n.GetName(), n.GetExternalNodeId())
42+
thisIdx = i
43+
} else {
44+
labels[i] = fmt.Sprintf("%s (%s)", n.GetName(), n.GetExternalNodeId())
4245
}
4346
}
47+
if thisIdx > 0 {
48+
nodes[0], nodes[thisIdx] = nodes[thisIdx], nodes[0]
49+
labels[0], labels[thisIdx] = labels[thisIdx], labels[0]
50+
}
4451
chosen := prompter.Select("Select node", labels)
4552
var selected *nodev1.ExternalNode
4653
for i, label := range labels {

pkg/cmd/register/sshkeys_test.go

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,16 @@
11
package register
22

33
import (
4+
"context"
45
"os"
56
"os/user"
67
"path/filepath"
78
"strings"
89
"testing"
10+
11+
nodev1 "buf.build/gen/go/brevdev/devplane/protocolbuffers/go/devplaneapi/v1"
12+
13+
"github.com/brevdev/brev-cli/pkg/terminal"
914
)
1015

1116
func tempUser(t *testing.T) *user.User {
@@ -230,3 +235,98 @@ func TestInstallAuthorizedKey_EmptyUserID_UsesPrefix(t *testing.T) {
230235
t.Errorf("should not contain user_id when empty, got:\n%s", content)
231236
}
232237
}
238+
239+
// --- SelectNodeFromList ---
240+
241+
// captureSelector records the items passed to Select and returns the item at returnIdx.
242+
type captureSelector struct {
243+
items []string
244+
returnIdx int
245+
}
246+
247+
func (s *captureSelector) Select(_ string, items []string) string {
248+
s.items = items
249+
return items[s.returnIdx]
250+
}
251+
252+
func makeNodes(ids ...string) []*nodev1.ExternalNode {
253+
nodes := make([]*nodev1.ExternalNode, len(ids))
254+
for i, id := range ids {
255+
nodes[i] = &nodev1.ExternalNode{
256+
ExternalNodeId: id,
257+
Name: "node-" + id,
258+
}
259+
}
260+
return nodes
261+
}
262+
263+
func TestSelectNodeFromList_ThisNodeMovedToFront(t *testing.T) {
264+
sel := &captureSelector{returnIdx: 0}
265+
regStore := &mockRegistrationStore{
266+
reg: &DeviceRegistration{ExternalNodeID: "c"},
267+
}
268+
nodes := makeNodes("a", "b", "c", "d")
269+
270+
got, err := SelectNodeFromList(context.Background(), terminal.New(), sel, regStore, nodes)
271+
if err != nil {
272+
t.Fatalf("unexpected error: %v", err)
273+
}
274+
275+
// "this node" label should be first in the list.
276+
if !strings.Contains(sel.items[0], "— this node") {
277+
t.Errorf("expected first item to be 'this node', got %q", sel.items[0])
278+
}
279+
// No other item should have the label.
280+
for _, item := range sel.items[1:] {
281+
if strings.Contains(item, "— this node") {
282+
t.Errorf("unexpected 'this node' label on non-first item: %q", item)
283+
}
284+
}
285+
// Selecting index 0 should return node "c".
286+
if got.GetExternalNodeId() != "c" {
287+
t.Errorf("expected selected node 'c', got %q", got.GetExternalNodeId())
288+
}
289+
}
290+
291+
func TestSelectNodeFromList_ThisNodeAlreadyFirst(t *testing.T) {
292+
sel := &captureSelector{returnIdx: 0}
293+
regStore := &mockRegistrationStore{
294+
reg: &DeviceRegistration{ExternalNodeID: "a"},
295+
}
296+
nodes := makeNodes("a", "b", "c")
297+
298+
got, err := SelectNodeFromList(context.Background(), terminal.New(), sel, regStore, nodes)
299+
if err != nil {
300+
t.Fatalf("unexpected error: %v", err)
301+
}
302+
303+
if !strings.Contains(sel.items[0], "— this node") {
304+
t.Errorf("expected first item to be 'this node', got %q", sel.items[0])
305+
}
306+
if got.GetExternalNodeId() != "a" {
307+
t.Errorf("expected selected node 'a', got %q", got.GetExternalNodeId())
308+
}
309+
}
310+
311+
func TestSelectNodeFromList_NoRegistration(t *testing.T) {
312+
sel := &captureSelector{returnIdx: 1}
313+
regStore := &mockRegistrationStore{} // no registration
314+
315+
nodes := makeNodes("a", "b", "c")
316+
317+
got, err := SelectNodeFromList(context.Background(), terminal.New(), sel, regStore, nodes)
318+
if err != nil {
319+
t.Fatalf("unexpected error: %v", err)
320+
}
321+
322+
// No item should have the "this node" label.
323+
for _, item := range sel.items {
324+
if strings.Contains(item, "— this node") {
325+
t.Errorf("unexpected 'this node' label: %q", item)
326+
}
327+
}
328+
// Order should be unchanged; selecting index 1 returns "b".
329+
if got.GetExternalNodeId() != "b" {
330+
t.Errorf("expected selected node 'b', got %q", got.GetExternalNodeId())
331+
}
332+
}

0 commit comments

Comments
 (0)