package cli import ( "os" "path/filepath" "reflect" "testing" "banger/internal/model" ) func TestNewBangerCommandHasExpectedSubcommands(t *testing.T) { cmd := NewBangerCommand() names := []string{} for _, sub := range cmd.Commands() { names = append(names, sub.Name()) } want := []string{"daemon", "image", "tui", "vm"} if !reflect.DeepEqual(names, want) { t.Fatalf("subcommands = %v, want %v", names, want) } } func TestVMCreateFlagsExist(t *testing.T) { root := NewBangerCommand() vm, _, err := root.Find([]string{"vm"}) if err != nil { t.Fatalf("find vm: %v", err) } create, _, err := vm.Find([]string{"create"}) if err != nil { t.Fatalf("find create: %v", err) } for _, flagName := range []string{"name", "image", "vcpu", "memory", "system-overlay-size", "disk-size", "nat", "no-start"} { if create.Flags().Lookup(flagName) == nil { t.Fatalf("missing flag %q", flagName) } } } func TestVMSetParamsFromFlags(t *testing.T) { params, err := vmSetParamsFromFlags("devbox", 4, 2048, "16G", true, false) if err != nil { t.Fatalf("vmSetParamsFromFlags: %v", err) } if params.IDOrName != "devbox" || params.VCPUCount == nil || *params.VCPUCount != 4 { t.Fatalf("unexpected params: %+v", params) } if params.MemoryMiB == nil || *params.MemoryMiB != 2048 { t.Fatalf("unexpected memory: %+v", params) } if params.WorkDiskSize != "16G" { t.Fatalf("unexpected disk size: %+v", params) } if params.NATEnabled == nil || !*params.NATEnabled { t.Fatalf("unexpected nat value: %+v", params) } } func TestVMSetParamsFromFlagsConflict(t *testing.T) { if _, err := vmSetParamsFromFlags("devbox", -1, -1, "", true, true); err == nil { t.Fatal("expected nat conflict error") } } func TestSSHCommandArgs(t *testing.T) { args, err := sshCommandArgs(model.DaemonConfig{RepoRoot: "/repo"}, "172.16.0.2", []string{"--", "uname", "-a"}) if err != nil { t.Fatalf("sshCommandArgs: %v", err) } want := []string{ "-i", "/repo/id_ed25519", "-o", "StrictHostKeyChecking=no", "-o", "UserKnownHostsFile=/dev/null", "root@172.16.0.2", "--", "uname", "-a", } if !reflect.DeepEqual(args, want) { t.Fatalf("args = %v, want %v", args, want) } } func TestNewBangerdCommandRejectsArgs(t *testing.T) { cmd := NewBangerdCommand() cmd.SetArgs([]string{"extra"}) if err := cmd.Execute(); err == nil { t.Fatal("expected extra args to be rejected") } } func TestDaemonOutdated(t *testing.T) { dir := t.TempDir() current := filepath.Join(dir, "bangerd-current") same := filepath.Join(dir, "bangerd-same") stale := filepath.Join(dir, "bangerd-stale") if err := os.WriteFile(current, []byte("current"), 0o755); err != nil { t.Fatalf("write current: %v", err) } if err := os.Link(current, same); err != nil { t.Fatalf("hard link: %v", err) } if err := os.WriteFile(stale, []byte("stale"), 0o755); err != nil { t.Fatalf("write stale: %v", err) } origBangerdPath := bangerdPathFunc origDaemonExePath := daemonExePath t.Cleanup(func() { bangerdPathFunc = origBangerdPath daemonExePath = origDaemonExePath }) bangerdPathFunc = func() (string, error) { return current, nil } daemonExePath = func(pid int) string { if pid == 1 { return same } return stale } if daemonOutdated(1) { t.Fatal("expected matching daemon executable to be current") } if !daemonOutdated(2) { t.Fatal("expected replaced daemon executable to be outdated") } }