Files
simslim-mirror/app_test.go
Interlap cf7080eb2f refactor: parse CLI with urfave/cli/v3
Replace the hand-rolled argument handling with the urfave/cli/v3 command
tree (the project's only dependency; no runtime transitive deps). This
removes the manual command switch, parseInterspersedFlags, jsonOption, and
extractDeviceSets in favor of the library's dispatch and flag parsing.

The command surface is unchanged: same commands, flags, arguments, JSON
output, error format (simslim: <msg>, exit 1), and unknown-command/usage
handling (exit 2). Flags may still appear before or after positional args
(v3 parses flags anywhere by default). --set is now a global flag inherited
by every subcommand, registered in its flag Action with last-wins,
comma-separated semantics. main still owns version, help, and the macOS
guard; the root usage text is preserved verbatim.

The command tree lives in app.go (newApp); each subcommand's Action is a
cmd* function in main.go reading flags via cmd.Bool/String and positionals
via cmd.Args(). Unit tests for the removed helpers are replaced by
app_test.go, which drives newApp through Run.
2026-07-22 13:16:41 +02:00

94 lines
3.7 KiB
Go

package main
import (
"context"
"os"
"reflect"
"testing"
)
// runApp runs the CLI with stdout/stderr discarded so command output does not
// pollute the test log. It uses `profiles`, which touches neither simctl nor the
// macOS-only guard, to exercise flag and argument parsing through the real tree.
func runApp(t *testing.T, args ...string) error {
t.Helper()
devnull, err := os.OpenFile(os.DevNull, os.O_WRONLY, 0)
if err != nil {
t.Fatalf("open %s: %v", os.DevNull, err)
}
defer devnull.Close()
oldStdout, oldStderr := os.Stdout, os.Stderr
os.Stdout, os.Stderr = devnull, devnull
defer func() { os.Stdout, os.Stderr = oldStdout, oldStderr }()
return newApp().Run(context.Background(), append([]string{"simslim"}, args...))
}
// TestAppRegistersDeviceSets verifies the global --set flag registers the extra
// device sets, whether it appears before or after the subcommand, in equals
// form, comma-separated, or repeated (last-wins), and that a blank value errors.
func TestAppRegistersDeviceSets(t *testing.T) {
tests := []struct {
name string
args []string
wantExtra []string // tokens registered beyond the well-known sets
wantError bool
}{
{name: "absent", args: []string{"profiles"}},
{name: "before command", args: []string{"--set", "/a", "profiles"}, wantExtra: []string{"/a"}},
{name: "after command", args: []string{"profiles", "--set", "/a"}, wantExtra: []string{"/a"}},
{name: "equals form", args: []string{"profiles", "--set=/a"}, wantExtra: []string{"/a"}},
{name: "comma-separated", args: []string{"--set", "/a,/b", "profiles"}, wantExtra: []string{"/a", "/b"}},
{name: "comma trims blanks", args: []string{"--set=/a, ,/b", "profiles"}, wantExtra: []string{"/a", "/b"}},
{name: "repeat is last-wins", args: []string{"--set", "/a", "--set", "/b", "profiles"}, wantExtra: []string{"/b"}},
{name: "well-known not duplicated", args: []string{"--set", "testing", "profiles"}},
{name: "blank value errors", args: []string{"--set", " ", "profiles"}, wantError: true},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
extraDeviceSets = nil
defer func() { extraDeviceSets = nil }()
err := runApp(t, tt.args...)
if (err != nil) != tt.wantError {
t.Fatalf("Run(%v) error = %v, wantError %v", tt.args, err, tt.wantError)
}
var gotExtra []string
for _, set := range extraDeviceSets {
gotExtra = append(gotExtra, set.token)
}
if !reflect.DeepEqual(gotExtra, tt.wantExtra) {
t.Errorf("registered extra sets = %v, want %v", gotExtra, tt.wantExtra)
}
})
}
}
// TestAppFlagParsing covers per-command flag parsing: a flag may appear before
// or after a positional argument, --json rejects duplicates, an unknown flag is
// rejected, and an unknown positional fails validation.
func TestAppFlagParsing(t *testing.T) {
tests := []struct {
name string
args []string
wantError bool
}{
{name: "json flag", args: []string{"profiles", "--json"}},
{name: "flag before positional", args: []string{"profiles", "--json", "siri"}},
{name: "flag after positional", args: []string{"profiles", "siri", "--json"}},
{name: "duplicate json rejected", args: []string{"profiles", "--json", "--json", "siri"}, wantError: true},
{name: "unknown flag rejected", args: []string{"profiles", "--nope"}, wantError: true},
{name: "unknown category rejected", args: []string{"profiles", "does-not-exist"}, wantError: true},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
extraDeviceSets = nil
defer func() { extraDeviceSets = nil }()
err := runApp(t, tt.args...)
if (err != nil) != tt.wantError {
t.Fatalf("Run(%v) error = %v, wantError %v", tt.args, err, tt.wantError)
}
})
}
}