e030cd28e9
Builder (cli.For[C]): Root/Dir/Cmd/SlotDir/Slot construct the tree without repeating the [C] type parameter at every node; returns plain *Node[C] so it interoperates with struct-literal construction. App[C]: collapses the per-binary main.go — standard flags (-color/-json/-version, -server when configured), mode-aware color defaults, version banner, client connect, and the one-shot-vs-shell split — into one Main(). Transport-agnostic via a Connect callback, so it never assumes gRPC. Makefile: `make check` = fixstyle vet lint test (the pre-commit gate), plus build and a linux+openbsd cross target. The example now dogfoods both Builder and App. Tests cover the builder tree and App's one-shot dispatch / -version / nil-Connect paths. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
80 lines
2.2 KiB
Go
80 lines
2.2 KiB
Go
// SPDX-FileCopyrightText: (C) Copyright 2026 Pim van Pelt <pim@ipng.ch>
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
|
|
package cli
|
|
|
|
import (
|
|
"context"
|
|
"testing"
|
|
)
|
|
|
|
// TestAppOneShotDispatch runs a one-shot command through App.Run and checks the
|
|
// positional args reach the command, the captured slot arg is correct, and the
|
|
// client from Connect is threaded through.
|
|
func TestAppOneShotDispatch(t *testing.T) {
|
|
var gotArg string
|
|
var gotClient fakeClient
|
|
b := For[fakeClient]()
|
|
root := b.Root(
|
|
b.Dir("ping", "",
|
|
b.Slot("<name>", "", func(context.Context, fakeClient, []string) []string { return nil },
|
|
func(_ context.Context, c fakeClient, args []string) error {
|
|
gotClient = c
|
|
gotArg = args[0]
|
|
return nil
|
|
})),
|
|
)
|
|
app := &App[fakeClient]{
|
|
Name: "t",
|
|
Root: root,
|
|
Connect: func(context.Context, string) (fakeClient, func(), error) {
|
|
return fakeClient{instances: []string{"sentinel"}}, nil, nil
|
|
},
|
|
}
|
|
if err := app.Run(context.Background(), []string{"ping", "host9"}); err != nil {
|
|
t.Fatalf("Run: %v", err)
|
|
}
|
|
if gotArg != "host9" {
|
|
t.Errorf("arg = %q, want host9", gotArg)
|
|
}
|
|
if len(gotClient.instances) != 1 || gotClient.instances[0] != "sentinel" {
|
|
t.Errorf("client not threaded from Connect: %+v", gotClient)
|
|
}
|
|
}
|
|
|
|
// TestAppVersion checks -version short-circuits before connecting.
|
|
func TestAppVersion(t *testing.T) {
|
|
connected := false
|
|
app := &App[fakeClient]{
|
|
Name: "t",
|
|
Version: "9.9.9",
|
|
Root: For[fakeClient]().Root(),
|
|
Connect: func(context.Context, string) (fakeClient, func(), error) {
|
|
connected = true
|
|
return fakeClient{}, nil, nil
|
|
},
|
|
}
|
|
if err := app.Run(context.Background(), []string{"-version"}); err != nil {
|
|
t.Fatalf("Run -version: %v", err)
|
|
}
|
|
if connected {
|
|
t.Error("-version should not connect")
|
|
}
|
|
}
|
|
|
|
// TestAppNilConnect checks a local CLI with no Connect uses the zero client.
|
|
func TestAppNilConnect(t *testing.T) {
|
|
ran := false
|
|
b := For[fakeClient]()
|
|
app := &App[fakeClient]{
|
|
Name: "t",
|
|
Root: b.Root(b.Cmd("go", "", func(context.Context, fakeClient, []string) error { ran = true; return nil })),
|
|
}
|
|
if err := app.Run(context.Background(), []string{"go"}); err != nil {
|
|
t.Fatalf("Run: %v", err)
|
|
}
|
|
if !ran {
|
|
t.Error("command did not run with nil Connect")
|
|
}
|
|
}
|