Teach VM listing Docker-style aliases and filters

Make `banger ps` a true alias of `banger vm list` and add `banger vm ls`
and `banger vm ps` so the common listing entrypoints all share one path.

Default the shared list command to running VMs only, add `--all` to include
stopped entries, `--latest` to keep only the newest matching VM, and `--quiet`
to print full VM IDs without the table renderer.

Cover the alias wiring plus the running/latest/quiet helpers in CLI tests.
Validation: go test ./internal/cli; GOCACHE=/tmp/banger-gocache go test ./...;
make build; ./build/bin/banger ps --help; ./build/bin/banger vm ls --help.
This commit is contained in:
Thales Maciel 2026-03-31 13:03:12 -03:00
parent 671723a0ef
commit dbc70643c3
No known key found for this signature in database
GPG key ID: 33112E6833C34679
2 changed files with 164 additions and 19 deletions

View file

@ -27,7 +27,7 @@ func TestNewBangerCommandHasExpectedSubcommands(t *testing.T) {
for _, sub := range cmd.Commands() {
names = append(names, sub.Name())
}
want := []string{"daemon", "doctor", "image", "internal", "version", "vm"}
want := []string{"daemon", "doctor", "image", "internal", "ps", "version", "vm"}
if !reflect.DeepEqual(names, want) {
t.Fatalf("subcommands = %v, want %v", names, want)
}
@ -155,6 +155,47 @@ func TestInternalPackagesCommandSupportsAlpine(t *testing.T) {
}
}
func TestPSAndVMListAliasesAndFlagsExist(t *testing.T) {
root := NewBangerCommand()
ps, _, err := root.Find([]string{"ps"})
if err != nil {
t.Fatalf("find ps: %v", err)
}
for _, flagName := range []string{"all", "latest", "quiet"} {
if ps.Flags().Lookup(flagName) == nil {
t.Fatalf("missing ps flag %q", flagName)
}
}
vm, _, err := root.Find([]string{"vm"})
if err != nil {
t.Fatalf("find vm: %v", err)
}
list, _, err := vm.Find([]string{"list"})
if err != nil {
t.Fatalf("find list: %v", err)
}
if _, _, err := vm.Find([]string{"ls"}); err != nil {
t.Fatalf("find ls alias: %v", err)
}
if _, _, err := vm.Find([]string{"ps"}); err != nil {
t.Fatalf("find ps alias: %v", err)
}
for _, flagName := range []string{"all", "latest", "quiet"} {
if list.Flags().Lookup(flagName) == nil {
t.Fatalf("missing vm list flag %q", flagName)
}
}
}
func TestPSCommandRejectsArgs(t *testing.T) {
cmd := NewBangerCommand()
cmd.SetArgs([]string{"ps", "extra"})
err := cmd.Execute()
if err == nil || !strings.Contains(err.Error(), "usage: banger ps") {
t.Fatalf("Execute() error = %v, want ps usage error", err)
}
}
func TestVMCreateFlagsExist(t *testing.T) {
root := NewBangerCommand()
vm, _, err := root.Find([]string{"vm"})
@ -595,6 +636,49 @@ func TestPrintImageListTableShowsRootfsSizes(t *testing.T) {
}
}
func TestSelectVMListVMsDefaultsToRunning(t *testing.T) {
now := time.Now()
vms := []model.VMRecord{
{ID: "running-1", State: model.VMStateRunning, CreatedAt: now.Add(-3 * time.Hour)},
{ID: "stopped-1", State: model.VMStateStopped, CreatedAt: now.Add(-2 * time.Hour)},
{ID: "running-2", State: model.VMStateRunning, CreatedAt: now.Add(-1 * time.Hour)},
}
got := selectVMListVMs(vms, false, false)
if len(got) != 2 || got[0].ID != "running-1" || got[1].ID != "running-2" {
t.Fatalf("selectVMListVMs() = %#v, want only running VMs in original order", got)
}
}
func TestSelectVMListVMsLatestUsesFilteredSet(t *testing.T) {
now := time.Now()
vms := []model.VMRecord{
{ID: "running-old", State: model.VMStateRunning, CreatedAt: now.Add(-3 * time.Hour)},
{ID: "stopped-new", State: model.VMStateStopped, CreatedAt: now.Add(-30 * time.Minute)},
{ID: "running-new", State: model.VMStateRunning, CreatedAt: now.Add(-1 * time.Hour)},
}
got := selectVMListVMs(vms, false, true)
if len(got) != 1 || got[0].ID != "running-new" {
t.Fatalf("selectVMListVMs(default latest) = %#v, want latest running VM", got)
}
got = selectVMListVMs(vms, true, true)
if len(got) != 1 || got[0].ID != "stopped-new" {
t.Fatalf("selectVMListVMs(all latest) = %#v, want latest VM across all states", got)
}
}
func TestPrintVMIDListShowsFullIDs(t *testing.T) {
var out bytes.Buffer
err := printVMIDList(&out, []model.VMRecord{{ID: "0123456789abcdef0123456789abcdef"}, {ID: "fedcba9876543210fedcba9876543210"}})
if err != nil {
t.Fatalf("printVMIDList() error = %v", err)
}
lines := strings.Split(strings.TrimSpace(out.String()), "\n")
want := []string{"0123456789abcdef0123456789abcdef", "fedcba9876543210fedcba9876543210"}
if !reflect.DeepEqual(lines, want) {
t.Fatalf("lines = %v, want %v", lines, want)
}
}
func TestPrintVMListTableShowsImageNames(t *testing.T) {
var out bytes.Buffer
err := printVMListTable(&out, []model.VMRecord{