test(cli): add basic command tests
- Add tests for CLI command tree and flag parsing - Align LDFLAGS injection path for version info - Remove unused manual help function
This commit is contained in:
parent
a2db707373
commit
f2e5d6ca5d
34 changed files with 1335 additions and 41 deletions
|
|
@ -15,10 +15,10 @@ builds:
|
||||||
- stdjson
|
- stdjson
|
||||||
ldflags:
|
ldflags:
|
||||||
- -s -w
|
- -s -w
|
||||||
- -X main.version={{ .Version }}
|
- -X github.com/sipeed/picoclaw/cmd/picoclaw/internal.version={{ .Version }}
|
||||||
- -X main.gitCommit={{ .ShortCommit }}
|
- -X github.com/sipeed/picoclaw/cmd/picoclaw/internal.gitCommit={{ .ShortCommit }}
|
||||||
- -X main.buildTime={{ .Date }}
|
- -X github.com/sipeed/picoclaw/cmd/picoclaw/internal.buildTime={{ .Date }}
|
||||||
- -X main.goVersion={{ .Env.GOVERSION }}
|
- -X github.com/sipeed/picoclaw/cmd/picoclaw/internal.goVersion={{ .Env.GOVERSION }}
|
||||||
goos:
|
goos:
|
||||||
- linux
|
- linux
|
||||||
- windows
|
- windows
|
||||||
|
|
|
||||||
3
Makefile
3
Makefile
|
|
@ -11,7 +11,8 @@ VERSION?=$(shell git describe --tags --always --dirty 2>/dev/null || echo "dev")
|
||||||
GIT_COMMIT=$(shell git rev-parse --short=8 HEAD 2>/dev/null || echo "dev")
|
GIT_COMMIT=$(shell git rev-parse --short=8 HEAD 2>/dev/null || echo "dev")
|
||||||
BUILD_TIME=$(shell date +%FT%T%z)
|
BUILD_TIME=$(shell date +%FT%T%z)
|
||||||
GO_VERSION=$(shell $(GO) version | awk '{print $$3}')
|
GO_VERSION=$(shell $(GO) version | awk '{print $$3}')
|
||||||
LDFLAGS=-ldflags "-X github.com/sipeed/picoclaw/cmd/picoclaw/internal.version=$(VERSION) -X github.com/sipeed/picoclaw/cmd/picoclaw/internal.gitCommit=$(GIT_COMMIT) -X github.com/sipeed/picoclaw/cmd/picoclaw/internal.buildTime=$(BUILD_TIME) -X github.com/sipeed/picoclaw/cmd/picoclaw/internal.goVersion=$(GO_VERSION) -s -w"
|
INTERNAL=github.com/sipeed/picoclaw/cmd/picoclaw/internal
|
||||||
|
LDFLAGS=-ldflags "-X $(INTERNAL).version=$(VERSION) -X $(INTERNAL).gitCommit=$(GIT_COMMIT) -X $(INTERNAL).buildTime=$(BUILD_TIME) -X $(INTERNAL).goVersion=$(GO_VERSION) -s -w"
|
||||||
|
|
||||||
# Go variables
|
# Go variables
|
||||||
GO?=go
|
GO?=go
|
||||||
|
|
|
||||||
59
cmd/picoclaw/internal/agent/command_test.go
Normal file
59
cmd/picoclaw/internal/agent/command_test.go
Normal file
|
|
@ -0,0 +1,59 @@
|
||||||
|
package agent
|
||||||
|
|
||||||
|
import "testing"
|
||||||
|
|
||||||
|
func TestNewAgentCommand(t *testing.T) {
|
||||||
|
cmd := NewAgentCommand()
|
||||||
|
|
||||||
|
if cmd == nil {
|
||||||
|
t.Fatalf("expected non-nil command")
|
||||||
|
}
|
||||||
|
|
||||||
|
if cmd.Use != "agent" {
|
||||||
|
t.Errorf("expected command name 'agent', got %q", cmd.Use)
|
||||||
|
}
|
||||||
|
|
||||||
|
if cmd.Short != "Interact with the agent directly" {
|
||||||
|
t.Errorf("expected command short description, got %q", cmd.Short)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(cmd.Aliases) > 0 {
|
||||||
|
t.Errorf("expected command to have no aliases, got %d", len(cmd.Aliases))
|
||||||
|
}
|
||||||
|
|
||||||
|
if cmd.HasSubCommands() {
|
||||||
|
t.Error("expected command to have no subcommands")
|
||||||
|
}
|
||||||
|
|
||||||
|
if cmd.Run != nil {
|
||||||
|
t.Error("expected command to have nil Run()")
|
||||||
|
}
|
||||||
|
|
||||||
|
if cmd.RunE == nil {
|
||||||
|
t.Error("expected command to have non-nil RunE()")
|
||||||
|
}
|
||||||
|
|
||||||
|
if cmd.PersistentPreRun != nil {
|
||||||
|
t.Error("expected command to have nil PersistentPreRun()")
|
||||||
|
}
|
||||||
|
|
||||||
|
if cmd.PersistentPostRun != nil {
|
||||||
|
t.Error("expected command to have nil PersistentPostRun()")
|
||||||
|
}
|
||||||
|
|
||||||
|
if !cmd.HasFlags() {
|
||||||
|
t.Error("expected command to have flags")
|
||||||
|
}
|
||||||
|
|
||||||
|
if cmd.Flags().Lookup("debug") == nil {
|
||||||
|
t.Error("expected command to have debug flag")
|
||||||
|
}
|
||||||
|
|
||||||
|
if cmd.Flags().Lookup("message") == nil {
|
||||||
|
t.Error("expected command to have message flag")
|
||||||
|
}
|
||||||
|
|
||||||
|
if cmd.Flags().Lookup("session") == nil {
|
||||||
|
t.Error("expected command to have session flag")
|
||||||
|
}
|
||||||
|
}
|
||||||
87
cmd/picoclaw/internal/auth/command_test.go
Normal file
87
cmd/picoclaw/internal/auth/command_test.go
Normal file
|
|
@ -0,0 +1,87 @@
|
||||||
|
package auth
|
||||||
|
|
||||||
|
import "testing"
|
||||||
|
|
||||||
|
func TestNewAuthCommand(t *testing.T) {
|
||||||
|
cmd := NewAuthCommand()
|
||||||
|
|
||||||
|
if cmd == nil {
|
||||||
|
t.Fatalf("expected non-nil command")
|
||||||
|
}
|
||||||
|
|
||||||
|
if cmd.Use != "auth" {
|
||||||
|
t.Errorf("expected command name 'auth', got %q", cmd.Use)
|
||||||
|
}
|
||||||
|
|
||||||
|
if cmd.Short != "Manage authentication (login, logout, status)" {
|
||||||
|
t.Errorf("expected command short description, got %q", cmd.Short)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(cmd.Aliases) > 0 {
|
||||||
|
t.Errorf("expected command to have no aliases, got %d", len(cmd.Aliases))
|
||||||
|
}
|
||||||
|
|
||||||
|
if cmd.Run != nil {
|
||||||
|
t.Error("expected command to have nil Run()")
|
||||||
|
}
|
||||||
|
|
||||||
|
if cmd.RunE == nil {
|
||||||
|
t.Error("expected command to have non-nil RunE()")
|
||||||
|
}
|
||||||
|
|
||||||
|
if cmd.PersistentPreRun != nil {
|
||||||
|
t.Error("expected command to have nil PersistentPreRun()")
|
||||||
|
}
|
||||||
|
|
||||||
|
if cmd.PersistentPostRun != nil {
|
||||||
|
t.Error("expected command to have nil PersistentPostRun()")
|
||||||
|
}
|
||||||
|
|
||||||
|
if cmd.HasFlags() {
|
||||||
|
t.Error("expected command to have no flags")
|
||||||
|
}
|
||||||
|
|
||||||
|
if !cmd.HasSubCommands() {
|
||||||
|
t.Error("expected command to have subcommands")
|
||||||
|
}
|
||||||
|
|
||||||
|
allowedCommands := map[string]struct{}{
|
||||||
|
"login": {},
|
||||||
|
"logout": {},
|
||||||
|
"status": {},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, subcmd := range cmd.Commands() {
|
||||||
|
if _, found := allowedCommands[subcmd.Name()]; !found {
|
||||||
|
t.Errorf("unexpected subcommand %q", subcmd.Name())
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(subcmd.Aliases) > 0 {
|
||||||
|
t.Errorf("expected subcommand %q to have no aliases, got %d", subcmd.Name(), len(subcmd.Aliases))
|
||||||
|
}
|
||||||
|
|
||||||
|
if cmd.Hidden {
|
||||||
|
t.Errorf("expected subcommand %q to be visible", subcmd.Name())
|
||||||
|
}
|
||||||
|
|
||||||
|
if subcmd.HasSubCommands() {
|
||||||
|
t.Errorf("expected subcommand `%s` to have no subcommands", subcmd.Name())
|
||||||
|
}
|
||||||
|
|
||||||
|
if subcmd.Run != nil {
|
||||||
|
t.Errorf("expected subcommand `%s` to have nil Run()", subcmd.Name())
|
||||||
|
}
|
||||||
|
|
||||||
|
if subcmd.RunE == nil {
|
||||||
|
t.Errorf("expected subcommand `%s` to have non-nil RunE()", subcmd.Name())
|
||||||
|
}
|
||||||
|
|
||||||
|
if subcmd.PersistentPreRun != nil {
|
||||||
|
t.Errorf("expected subcommand `%s` to have nil PersistentPreRun()", subcmd.Name())
|
||||||
|
}
|
||||||
|
|
||||||
|
if subcmd.PersistentPostRun != nil {
|
||||||
|
t.Errorf("expected subcommand `%s` to have nil PersistentPostRun()", subcmd.Name())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -9,24 +9,6 @@ import (
|
||||||
"github.com/sipeed/picoclaw/pkg/config"
|
"github.com/sipeed/picoclaw/pkg/config"
|
||||||
)
|
)
|
||||||
|
|
||||||
func authHelp() {
|
|
||||||
fmt.Println("\nAuth commands:")
|
|
||||||
fmt.Println(" login Login via OAuth or paste token")
|
|
||||||
fmt.Println(" logout Remove stored credentials")
|
|
||||||
fmt.Println(" status Show current auth status")
|
|
||||||
fmt.Println()
|
|
||||||
fmt.Println("Login options:")
|
|
||||||
fmt.Println(" --provider <name> Provider to login with (openai, anthropic)")
|
|
||||||
fmt.Println(" --device-code Use device code flow (for headless environments)")
|
|
||||||
fmt.Println()
|
|
||||||
fmt.Println("Examples:")
|
|
||||||
fmt.Println(" picoclaw auth login --provider openai")
|
|
||||||
fmt.Println(" picoclaw auth login --provider openai --device-code")
|
|
||||||
fmt.Println(" picoclaw auth login --provider anthropic")
|
|
||||||
fmt.Println(" picoclaw auth logout --provider openai")
|
|
||||||
fmt.Println(" picoclaw auth status")
|
|
||||||
}
|
|
||||||
|
|
||||||
func authLoginCmd(provider string, useDeviceCode bool) error {
|
func authLoginCmd(provider string, useDeviceCode bool) error {
|
||||||
switch provider {
|
switch provider {
|
||||||
case "openai":
|
case "openai":
|
||||||
|
|
|
||||||
42
cmd/picoclaw/internal/auth/login_test.go
Normal file
42
cmd/picoclaw/internal/auth/login_test.go
Normal file
|
|
@ -0,0 +1,42 @@
|
||||||
|
package auth
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestNewLoginSubCommand(t *testing.T) {
|
||||||
|
cmd := newLoginCommand()
|
||||||
|
|
||||||
|
if cmd == nil {
|
||||||
|
t.Fatalf("expected non-nil command")
|
||||||
|
}
|
||||||
|
|
||||||
|
if cmd.Short != "Login via OAuth or paste token" {
|
||||||
|
t.Errorf("expected command short description, got %q", cmd.Short)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !cmd.HasFlags() {
|
||||||
|
t.Error("expected command to have flags")
|
||||||
|
}
|
||||||
|
|
||||||
|
if cmd.Flags().Lookup("device-code") == nil {
|
||||||
|
t.Error("expected command to have device-code flag")
|
||||||
|
}
|
||||||
|
|
||||||
|
hasProviderFlag := cmd.Flags().Lookup("provider") != nil
|
||||||
|
if !hasProviderFlag {
|
||||||
|
t.Error("expected command to have provider flag")
|
||||||
|
} else {
|
||||||
|
var val []string
|
||||||
|
var found bool
|
||||||
|
providerFlag := cmd.Flag("provider")
|
||||||
|
|
||||||
|
val, found = providerFlag.Annotations[cobra.BashCompOneRequiredFlag]
|
||||||
|
|
||||||
|
if !found || val[0] != "true" {
|
||||||
|
t.Errorf("expected provider flag to be required, got %v", val)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
23
cmd/picoclaw/internal/auth/logout_test.go
Normal file
23
cmd/picoclaw/internal/auth/logout_test.go
Normal file
|
|
@ -0,0 +1,23 @@
|
||||||
|
package auth
|
||||||
|
|
||||||
|
import "testing"
|
||||||
|
|
||||||
|
func TestNewLogoutSubcommand(t *testing.T) {
|
||||||
|
cmd := newLogoutCommand()
|
||||||
|
|
||||||
|
if cmd == nil {
|
||||||
|
t.Fatalf("expected non-nil command")
|
||||||
|
}
|
||||||
|
|
||||||
|
if cmd.Short != "Remove stored credentials" {
|
||||||
|
t.Errorf("expected command short description, got %q", cmd.Short)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !cmd.HasFlags() {
|
||||||
|
t.Error("expected command to have flags")
|
||||||
|
}
|
||||||
|
|
||||||
|
if cmd.Flags().Lookup("provider") == nil {
|
||||||
|
t.Error("expected command to have provider flag")
|
||||||
|
}
|
||||||
|
}
|
||||||
19
cmd/picoclaw/internal/auth/status_test.go
Normal file
19
cmd/picoclaw/internal/auth/status_test.go
Normal file
|
|
@ -0,0 +1,19 @@
|
||||||
|
package auth
|
||||||
|
|
||||||
|
import "testing"
|
||||||
|
|
||||||
|
func TestNewStatusSubcommand(t *testing.T) {
|
||||||
|
cmd := newStatusCommand()
|
||||||
|
|
||||||
|
if cmd == nil {
|
||||||
|
t.Fatalf("expected non-nil command")
|
||||||
|
}
|
||||||
|
|
||||||
|
if cmd.Short != "Show current auth status" {
|
||||||
|
t.Errorf("expected command short description, got %q", cmd.Short)
|
||||||
|
}
|
||||||
|
|
||||||
|
if cmd.HasFlags() {
|
||||||
|
t.Error("expected command to have no flags")
|
||||||
|
}
|
||||||
|
}
|
||||||
83
cmd/picoclaw/internal/cron/add_test.go
Normal file
83
cmd/picoclaw/internal/cron/add_test.go
Normal file
|
|
@ -0,0 +1,83 @@
|
||||||
|
package cron
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestNewAddSubcommand(t *testing.T) {
|
||||||
|
fn := func() string { return "" }
|
||||||
|
cmd := newAddCommand(fn)
|
||||||
|
|
||||||
|
if cmd == nil {
|
||||||
|
t.Fatalf("expected non-nil command")
|
||||||
|
}
|
||||||
|
|
||||||
|
if cmd.Use != "add" {
|
||||||
|
t.Errorf("expected command name 'add', got %q", cmd.Use)
|
||||||
|
}
|
||||||
|
|
||||||
|
if cmd.Short != "Add a new scheduled job" {
|
||||||
|
t.Errorf("expected command short description, got %q", cmd.Short)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !cmd.HasFlags() {
|
||||||
|
t.Error("expected command to have flags")
|
||||||
|
}
|
||||||
|
|
||||||
|
if cmd.Flags().Lookup("every") == nil {
|
||||||
|
t.Error("expected command to have every flag")
|
||||||
|
}
|
||||||
|
|
||||||
|
if cmd.Flags().Lookup("cron") == nil {
|
||||||
|
t.Error("expected command to have cron flag")
|
||||||
|
}
|
||||||
|
|
||||||
|
if cmd.Flags().Lookup("deliver") == nil {
|
||||||
|
t.Error("expected command to have deliver flag")
|
||||||
|
}
|
||||||
|
|
||||||
|
if cmd.Flags().Lookup("to") == nil {
|
||||||
|
t.Error("expected command to have to flag")
|
||||||
|
}
|
||||||
|
|
||||||
|
if cmd.Flags().Lookup("channel") == nil {
|
||||||
|
t.Error("expected command to have channel flag")
|
||||||
|
}
|
||||||
|
|
||||||
|
hasNameFlag := cmd.Flags().Lookup("name") != nil
|
||||||
|
hasMessageFlag := cmd.Flags().Lookup("message") != nil
|
||||||
|
|
||||||
|
if !hasNameFlag {
|
||||||
|
t.Error("expected command to have name flag")
|
||||||
|
}
|
||||||
|
|
||||||
|
if !hasMessageFlag {
|
||||||
|
t.Error("expected command to have message flag")
|
||||||
|
}
|
||||||
|
|
||||||
|
if hasNameFlag {
|
||||||
|
var val []string
|
||||||
|
var found bool
|
||||||
|
nameFlag := cmd.Flag("name")
|
||||||
|
|
||||||
|
val, found = nameFlag.Annotations[cobra.BashCompOneRequiredFlag]
|
||||||
|
|
||||||
|
if !found || val[0] != "true" {
|
||||||
|
t.Errorf("expected name flag to be required, got %v", val)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if hasMessageFlag {
|
||||||
|
var val []string
|
||||||
|
var found bool
|
||||||
|
messageFlag := cmd.Flag("message")
|
||||||
|
|
||||||
|
val, found = messageFlag.Annotations[cobra.BashCompOneRequiredFlag]
|
||||||
|
|
||||||
|
if !found || val[0] != "true" {
|
||||||
|
t.Errorf("expected message flag to be required, got %v", val)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -33,8 +33,8 @@ func NewCronCommand() *cobra.Command {
|
||||||
newListCommand(func() string { return storePath }),
|
newListCommand(func() string { return storePath }),
|
||||||
newAddCommand(func() string { return storePath }),
|
newAddCommand(func() string { return storePath }),
|
||||||
newRemoveCommand(func() string { return storePath }),
|
newRemoveCommand(func() string { return storePath }),
|
||||||
newEnableCommand(func() string { return storePath }, false),
|
newEnableCommand(func() string { return storePath }),
|
||||||
newEnableCommand(func() string { return storePath }, true),
|
newEnableCommand(func() string { return storePath }),
|
||||||
)
|
)
|
||||||
|
|
||||||
return cmd
|
return cmd
|
||||||
|
|
|
||||||
93
cmd/picoclaw/internal/cron/command_test.go
Normal file
93
cmd/picoclaw/internal/cron/command_test.go
Normal file
|
|
@ -0,0 +1,93 @@
|
||||||
|
package cron
|
||||||
|
|
||||||
|
import "testing"
|
||||||
|
|
||||||
|
func TestNewCronCommand(t *testing.T) {
|
||||||
|
cmd := NewCronCommand()
|
||||||
|
|
||||||
|
if cmd == nil {
|
||||||
|
t.Fatalf("expected non-nil command")
|
||||||
|
}
|
||||||
|
|
||||||
|
if cmd.Short != "Manage scheduled tasks" {
|
||||||
|
t.Errorf("expected command short description, got %q", cmd.Short)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(cmd.Aliases) != 1 {
|
||||||
|
t.Errorf("expected command to have exactly one alias, got %d", len(cmd.Aliases))
|
||||||
|
}
|
||||||
|
|
||||||
|
if !cmd.HasAlias("c") {
|
||||||
|
t.Errorf("expected command to have alias `c`, got %v", cmd.Aliases)
|
||||||
|
}
|
||||||
|
|
||||||
|
if cmd.HasFlags() {
|
||||||
|
t.Error("expected command to have no flags")
|
||||||
|
}
|
||||||
|
|
||||||
|
if cmd.Run != nil {
|
||||||
|
t.Error("expected command to have nil Run()")
|
||||||
|
}
|
||||||
|
|
||||||
|
if cmd.RunE == nil {
|
||||||
|
t.Error("expected command to have non-nil RunE()")
|
||||||
|
}
|
||||||
|
|
||||||
|
if cmd.PersistentPreRunE == nil {
|
||||||
|
t.Error("expected command to have non-nil PersistentPreRunE()")
|
||||||
|
}
|
||||||
|
|
||||||
|
if cmd.PersistentPreRun != nil {
|
||||||
|
t.Error("expected command to have nil PersistentPreRun()")
|
||||||
|
}
|
||||||
|
|
||||||
|
if cmd.PersistentPostRun != nil {
|
||||||
|
t.Error("expected command to have nil PersistentPostRun()")
|
||||||
|
}
|
||||||
|
|
||||||
|
if !cmd.HasSubCommands() {
|
||||||
|
t.Error("expected command to have subcommands")
|
||||||
|
}
|
||||||
|
|
||||||
|
allowedCommands := map[string]struct{}{
|
||||||
|
"list": {},
|
||||||
|
"add": {},
|
||||||
|
"remove": {},
|
||||||
|
"enable": {},
|
||||||
|
"disable": {},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, subcmd := range cmd.Commands() {
|
||||||
|
if _, found := allowedCommands[subcmd.Name()]; !found {
|
||||||
|
t.Errorf("unexpected subcommand %q", subcmd.Name())
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(subcmd.Aliases) > 0 {
|
||||||
|
t.Errorf("expected subcommand %q to have no aliases, got %d", subcmd.Name(), len(subcmd.Aliases))
|
||||||
|
}
|
||||||
|
|
||||||
|
if cmd.Hidden {
|
||||||
|
t.Errorf("expected subcommand %q to be visible", subcmd.Name())
|
||||||
|
}
|
||||||
|
|
||||||
|
if subcmd.HasSubCommands() {
|
||||||
|
t.Errorf("expected subcommand `%s` to have no subcommands", subcmd.Name())
|
||||||
|
}
|
||||||
|
|
||||||
|
if subcmd.Run != nil {
|
||||||
|
t.Errorf("expected subcommand `%s` to have nil Run()", subcmd.Name())
|
||||||
|
}
|
||||||
|
|
||||||
|
if subcmd.RunE == nil {
|
||||||
|
t.Errorf("expected subcommand `%s` to have non-nil RunE()", subcmd.Name())
|
||||||
|
}
|
||||||
|
|
||||||
|
if subcmd.PersistentPreRun != nil {
|
||||||
|
t.Errorf("expected subcommand `%s` to have nil PersistentPreRun()", subcmd.Name())
|
||||||
|
}
|
||||||
|
|
||||||
|
if subcmd.PersistentPostRun != nil {
|
||||||
|
t.Errorf("expected subcommand `%s` to have nil PersistentPostRun()", subcmd.Name())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
16
cmd/picoclaw/internal/cron/disable.go
Normal file
16
cmd/picoclaw/internal/cron/disable.go
Normal file
|
|
@ -0,0 +1,16 @@
|
||||||
|
package cron
|
||||||
|
|
||||||
|
import "github.com/spf13/cobra"
|
||||||
|
|
||||||
|
func newDisableCommand(storePath func() string) *cobra.Command {
|
||||||
|
return &cobra.Command{
|
||||||
|
Use: "disable",
|
||||||
|
Short: "Disable a job",
|
||||||
|
Args: cobra.ExactArgs(1),
|
||||||
|
Example: `picoclaw cron disable 1`,
|
||||||
|
RunE: func(_ *cobra.Command, args []string) error {
|
||||||
|
cronEnableCmd(storePath(), true, args[0])
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
24
cmd/picoclaw/internal/cron/disable_test.go
Normal file
24
cmd/picoclaw/internal/cron/disable_test.go
Normal file
|
|
@ -0,0 +1,24 @@
|
||||||
|
package cron
|
||||||
|
|
||||||
|
import "testing"
|
||||||
|
|
||||||
|
func TestDisableSubcommand(t *testing.T) {
|
||||||
|
fn := func() string { return "" }
|
||||||
|
cmd := newDisableCommand(fn)
|
||||||
|
|
||||||
|
if cmd == nil {
|
||||||
|
t.Fatalf("expected non-nil command")
|
||||||
|
}
|
||||||
|
|
||||||
|
if cmd.Use != "disable" {
|
||||||
|
t.Errorf("expected command name 'disable', got %q", cmd.Use)
|
||||||
|
}
|
||||||
|
|
||||||
|
if cmd.Short != "Disable a job" {
|
||||||
|
t.Errorf("expected command short description, got %q", cmd.Short)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !cmd.HasExample() {
|
||||||
|
t.Error("expected command to have example")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -2,20 +2,14 @@ package cron
|
||||||
|
|
||||||
import "github.com/spf13/cobra"
|
import "github.com/spf13/cobra"
|
||||||
|
|
||||||
func newEnableCommand(storePath func() string, disable bool) *cobra.Command {
|
func newEnableCommand(storePath func() string) *cobra.Command {
|
||||||
name := "enable"
|
|
||||||
short := "Enable a job"
|
|
||||||
if disable {
|
|
||||||
name = "disable"
|
|
||||||
short = "Disable a job"
|
|
||||||
}
|
|
||||||
|
|
||||||
return &cobra.Command{
|
return &cobra.Command{
|
||||||
Use: name + " <id>",
|
Use: "enable",
|
||||||
Short: short,
|
Short: "Enable a job",
|
||||||
Args: cobra.ExactArgs(1),
|
Args: cobra.ExactArgs(1),
|
||||||
|
Example: `picoclaw cron enable 1`,
|
||||||
RunE: func(_ *cobra.Command, args []string) error {
|
RunE: func(_ *cobra.Command, args []string) error {
|
||||||
cronEnableCmd(storePath(), disable, args[0])
|
cronEnableCmd(storePath(), false, args[0])
|
||||||
return nil
|
return nil
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
|
||||||
24
cmd/picoclaw/internal/cron/enable_test.go
Normal file
24
cmd/picoclaw/internal/cron/enable_test.go
Normal file
|
|
@ -0,0 +1,24 @@
|
||||||
|
package cron
|
||||||
|
|
||||||
|
import "testing"
|
||||||
|
|
||||||
|
func TestEnableSubcommand(t *testing.T) {
|
||||||
|
fn := func() string { return "" }
|
||||||
|
cmd := newEnableCommand(fn)
|
||||||
|
|
||||||
|
if cmd == nil {
|
||||||
|
t.Fatalf("expected non-nil command")
|
||||||
|
}
|
||||||
|
|
||||||
|
if cmd.Use != "enable" {
|
||||||
|
t.Errorf("expected command name 'enable', got %q", cmd.Use)
|
||||||
|
}
|
||||||
|
|
||||||
|
if cmd.Short != "Enable a job" {
|
||||||
|
t.Errorf("expected command short description, got %q", cmd.Short)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !cmd.HasExample() {
|
||||||
|
t.Error("expected command to have example")
|
||||||
|
}
|
||||||
|
}
|
||||||
16
cmd/picoclaw/internal/cron/list_test.go
Normal file
16
cmd/picoclaw/internal/cron/list_test.go
Normal file
|
|
@ -0,0 +1,16 @@
|
||||||
|
package cron
|
||||||
|
|
||||||
|
import "testing"
|
||||||
|
|
||||||
|
func TestNewListSubcommand(t *testing.T) {
|
||||||
|
fn := func() string { return "" }
|
||||||
|
cmd := newListCommand(fn)
|
||||||
|
|
||||||
|
if cmd == nil {
|
||||||
|
t.Fatalf("expected non-nil command")
|
||||||
|
}
|
||||||
|
|
||||||
|
if cmd.Short != "List all scheduled jobs" {
|
||||||
|
t.Errorf("expected command short description, got %q", cmd.Short)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -4,9 +4,10 @@ import "github.com/spf13/cobra"
|
||||||
|
|
||||||
func newRemoveCommand(storePath func() string) *cobra.Command {
|
func newRemoveCommand(storePath func() string) *cobra.Command {
|
||||||
cmd := &cobra.Command{
|
cmd := &cobra.Command{
|
||||||
Use: "remove <id>",
|
Use: "remove",
|
||||||
Short: "Remove a job by ID",
|
Short: "Remove a job by ID",
|
||||||
Args: cobra.ExactArgs(1),
|
Args: cobra.ExactArgs(1),
|
||||||
|
Example: `picoclaw cron remove 1`,
|
||||||
RunE: func(_ *cobra.Command, args []string) error {
|
RunE: func(_ *cobra.Command, args []string) error {
|
||||||
cronRemoveCmd(storePath(), args[0])
|
cronRemoveCmd(storePath(), args[0])
|
||||||
return nil
|
return nil
|
||||||
|
|
|
||||||
20
cmd/picoclaw/internal/cron/remove_test.go
Normal file
20
cmd/picoclaw/internal/cron/remove_test.go
Normal file
|
|
@ -0,0 +1,20 @@
|
||||||
|
package cron
|
||||||
|
|
||||||
|
import "testing"
|
||||||
|
|
||||||
|
func TestNewRemoveSubcommand(t *testing.T) {
|
||||||
|
fn := func() string { return "" }
|
||||||
|
cmd := newRemoveCommand(fn)
|
||||||
|
|
||||||
|
if cmd == nil {
|
||||||
|
t.Fatalf("expected non-nil command")
|
||||||
|
}
|
||||||
|
|
||||||
|
if cmd.Short != "Remove a job by ID" {
|
||||||
|
t.Errorf("expected command short description, got %q", cmd.Short)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !cmd.HasExample() {
|
||||||
|
t.Error("expected command to have example")
|
||||||
|
}
|
||||||
|
}
|
||||||
55
cmd/picoclaw/internal/gateway/command_test.go
Normal file
55
cmd/picoclaw/internal/gateway/command_test.go
Normal file
|
|
@ -0,0 +1,55 @@
|
||||||
|
package gateway
|
||||||
|
|
||||||
|
import "testing"
|
||||||
|
|
||||||
|
func TestNewGatewayCommand(t *testing.T) {
|
||||||
|
cmd := NewGatewayCommand()
|
||||||
|
|
||||||
|
if cmd == nil {
|
||||||
|
t.Fatalf("expected non-nil command")
|
||||||
|
}
|
||||||
|
|
||||||
|
if cmd.Use != "gateway" {
|
||||||
|
t.Errorf("expected command name 'gateway', got %q", cmd.Use)
|
||||||
|
}
|
||||||
|
|
||||||
|
if cmd.Short != "Start picoclaw gateway" {
|
||||||
|
t.Errorf("expected command short description, got %q", cmd.Short)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(cmd.Aliases) != 1 {
|
||||||
|
t.Errorf("expected command to have 1 alias, got %d", len(cmd.Aliases))
|
||||||
|
}
|
||||||
|
|
||||||
|
if !cmd.HasAlias("g") {
|
||||||
|
t.Errorf("expected command to have alias 'g'")
|
||||||
|
}
|
||||||
|
|
||||||
|
if cmd.Run != nil {
|
||||||
|
t.Error("expected command to have nil Run()")
|
||||||
|
}
|
||||||
|
|
||||||
|
if cmd.RunE == nil {
|
||||||
|
t.Error("expected command to have non-nil RunE()")
|
||||||
|
}
|
||||||
|
|
||||||
|
if cmd.PersistentPreRun != nil {
|
||||||
|
t.Error("expected command to have nil PersistentPreRun()")
|
||||||
|
}
|
||||||
|
|
||||||
|
if cmd.PersistentPostRun != nil {
|
||||||
|
t.Error("expected command to have nil PersistentPostRun()")
|
||||||
|
}
|
||||||
|
|
||||||
|
if cmd.HasSubCommands() {
|
||||||
|
t.Error("expected command to have no subcommands")
|
||||||
|
}
|
||||||
|
|
||||||
|
if !cmd.HasFlags() {
|
||||||
|
t.Error("expected command to have flags")
|
||||||
|
}
|
||||||
|
|
||||||
|
if cmd.Flags().Lookup("debug") == nil {
|
||||||
|
t.Error("expected command to have debug flag")
|
||||||
|
}
|
||||||
|
}
|
||||||
132
cmd/picoclaw/internal/helpers_test.go
Normal file
132
cmd/picoclaw/internal/helpers_test.go
Normal file
|
|
@ -0,0 +1,132 @@
|
||||||
|
package internal
|
||||||
|
|
||||||
|
import (
|
||||||
|
"path/filepath"
|
||||||
|
"runtime"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestGetConfigPath(t *testing.T) {
|
||||||
|
t.Setenv("HOME", "/tmp/home")
|
||||||
|
|
||||||
|
got := GetConfigPath()
|
||||||
|
want := filepath.Join("/tmp/home", ".picoclaw", "config.json")
|
||||||
|
|
||||||
|
if got != want {
|
||||||
|
t.Fatalf("GetConfigPath() = %q, want %q", got, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFormatVersion_NoGitCommit(t *testing.T) {
|
||||||
|
oldVersion, oldGit := version, gitCommit
|
||||||
|
|
||||||
|
t.Cleanup(func() {
|
||||||
|
version, gitCommit = oldVersion, oldGit
|
||||||
|
})
|
||||||
|
|
||||||
|
version = "1.2.3"
|
||||||
|
gitCommit = ""
|
||||||
|
|
||||||
|
got := FormatVersion()
|
||||||
|
want := "1.2.3"
|
||||||
|
|
||||||
|
if got != want {
|
||||||
|
t.Fatalf("FormatVersion() = %q, want %q", got, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFormatVersion_WithGitCommit(t *testing.T) {
|
||||||
|
oldVersion, oldGit := version, gitCommit
|
||||||
|
|
||||||
|
t.Cleanup(func() {
|
||||||
|
version, gitCommit = oldVersion, oldGit
|
||||||
|
})
|
||||||
|
|
||||||
|
version = "1.2.3"
|
||||||
|
gitCommit = "abc123"
|
||||||
|
|
||||||
|
got := FormatVersion()
|
||||||
|
want := "1.2.3 (git: abc123)"
|
||||||
|
|
||||||
|
if got != want {
|
||||||
|
t.Fatalf("FormatVersion() = %q, want %q", got, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFormatBuildInfo_UsesBuildTimeAndGoVersion_WhenSet(t *testing.T) {
|
||||||
|
oldBuildTime, oldGoVersion := buildTime, goVersion
|
||||||
|
|
||||||
|
t.Cleanup(func() {
|
||||||
|
buildTime, goVersion = oldBuildTime, oldGoVersion
|
||||||
|
})
|
||||||
|
|
||||||
|
buildTime = "2026-02-20T00:00:00Z"
|
||||||
|
goVersion = "go1.23.0"
|
||||||
|
|
||||||
|
build, goVer := FormatBuildInfo()
|
||||||
|
|
||||||
|
if build != buildTime {
|
||||||
|
t.Fatalf("FormatBuildInfo().build = %q, want %q", build, buildTime)
|
||||||
|
}
|
||||||
|
|
||||||
|
if goVer != goVersion {
|
||||||
|
t.Fatalf("FormatBuildInfo().goVer = %q, want %q", goVer, goVersion)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFormatBuildInfo_EmptyBuildTime_ReturnsEmptyBuild(t *testing.T) {
|
||||||
|
oldBuildTime, oldGoVersion := buildTime, goVersion
|
||||||
|
|
||||||
|
t.Cleanup(func() {
|
||||||
|
buildTime, goVersion = oldBuildTime, oldGoVersion
|
||||||
|
})
|
||||||
|
|
||||||
|
buildTime = ""
|
||||||
|
goVersion = "go1.23.0"
|
||||||
|
|
||||||
|
build, goVer := FormatBuildInfo()
|
||||||
|
|
||||||
|
if build != "" {
|
||||||
|
t.Fatalf("FormatBuildInfo().build = %q, want empty", build)
|
||||||
|
}
|
||||||
|
|
||||||
|
if goVer != goVersion {
|
||||||
|
t.Fatalf("FormatBuildInfo().goVer = %q, want %q", goVer, goVersion)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFormatBuildInfo_EmptyGoVersion_FallsBackToRuntimeVersion(t *testing.T) {
|
||||||
|
oldBuildTime, oldGoVersion := buildTime, goVersion
|
||||||
|
|
||||||
|
t.Cleanup(func() {
|
||||||
|
buildTime, goVersion = oldBuildTime, oldGoVersion
|
||||||
|
})
|
||||||
|
|
||||||
|
buildTime = "x"
|
||||||
|
goVersion = ""
|
||||||
|
|
||||||
|
build, goVer := FormatBuildInfo()
|
||||||
|
if build != "x" {
|
||||||
|
t.Fatalf("FormatBuildInfo().build = %q, want %q", build, "x")
|
||||||
|
}
|
||||||
|
|
||||||
|
if goVer != runtime.Version() {
|
||||||
|
t.Fatalf("FormatBuildInfo().goVer = %q, want runtime.Version()=%q", goVer, runtime.Version())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetConfigPath_Windows(t *testing.T) {
|
||||||
|
if runtime.GOOS != "windows" {
|
||||||
|
t.Skip("windows-specific HOME behavior varies; run on windows")
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Setenv("USERPROFILE", `C:\Users\Test`)
|
||||||
|
|
||||||
|
got := GetConfigPath()
|
||||||
|
want := filepath.Join(`C:\Users\Test`, ".picoclaw", "config.json")
|
||||||
|
|
||||||
|
if !strings.EqualFold(got, want) {
|
||||||
|
t.Fatalf("GetConfigPath() = %q, want %q", got, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
79
cmd/picoclaw/internal/migrate/command_test.go
Normal file
79
cmd/picoclaw/internal/migrate/command_test.go
Normal file
|
|
@ -0,0 +1,79 @@
|
||||||
|
package migrate
|
||||||
|
|
||||||
|
import "testing"
|
||||||
|
|
||||||
|
func TestNewMigrateCommand(t *testing.T) {
|
||||||
|
cmd := NewMigrateCommand()
|
||||||
|
|
||||||
|
if cmd == nil {
|
||||||
|
t.Fatalf("expected non-nil command")
|
||||||
|
}
|
||||||
|
|
||||||
|
if cmd.Use != "migrate" {
|
||||||
|
t.Errorf("expected command name 'migrate', got %q", cmd.Use)
|
||||||
|
}
|
||||||
|
|
||||||
|
if cmd.Short != "Migrate from OpenClaw to PicoClaw" {
|
||||||
|
t.Errorf("expected command short description, got %q", cmd.Short)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(cmd.Aliases) > 0 {
|
||||||
|
t.Errorf("expected command to have no aliases, got %d", len(cmd.Aliases))
|
||||||
|
}
|
||||||
|
|
||||||
|
if !cmd.HasExample() {
|
||||||
|
t.Error("expected command to have example")
|
||||||
|
}
|
||||||
|
|
||||||
|
if cmd.HasSubCommands() {
|
||||||
|
t.Error("expected command to have no subcommands")
|
||||||
|
}
|
||||||
|
|
||||||
|
if cmd.Run != nil {
|
||||||
|
t.Error("expected command to have nil Run()")
|
||||||
|
}
|
||||||
|
|
||||||
|
if cmd.RunE == nil {
|
||||||
|
t.Error("expected command to have non-nil RunE()")
|
||||||
|
}
|
||||||
|
|
||||||
|
if cmd.PersistentPreRun != nil {
|
||||||
|
t.Error("expected command to have nil PersistentPreRun()")
|
||||||
|
}
|
||||||
|
|
||||||
|
if cmd.PersistentPostRun != nil {
|
||||||
|
t.Error("expected command to have nil PersistentPostRun()")
|
||||||
|
}
|
||||||
|
|
||||||
|
if !cmd.HasFlags() {
|
||||||
|
t.Error("expected command to have flags")
|
||||||
|
}
|
||||||
|
|
||||||
|
if cmd.Flags().Lookup("dry-run") == nil {
|
||||||
|
t.Error("expected command to have dry-run flag")
|
||||||
|
}
|
||||||
|
|
||||||
|
if cmd.Flags().Lookup("refresh") == nil {
|
||||||
|
t.Error("expected command to have refresh flag")
|
||||||
|
}
|
||||||
|
|
||||||
|
if cmd.Flags().Lookup("config-only") == nil {
|
||||||
|
t.Error("expected command to have config-only flag")
|
||||||
|
}
|
||||||
|
|
||||||
|
if cmd.Flags().Lookup("workspace-only") == nil {
|
||||||
|
t.Error("expected command to have workspace-only flag")
|
||||||
|
}
|
||||||
|
|
||||||
|
if cmd.Flags().Lookup("force") == nil {
|
||||||
|
t.Error("expected command to have force flag")
|
||||||
|
}
|
||||||
|
|
||||||
|
if cmd.Flags().Lookup("openclaw-home") == nil {
|
||||||
|
t.Error("expected command to have openclaw-home flag")
|
||||||
|
}
|
||||||
|
|
||||||
|
if cmd.Flags().Lookup("picoclaw-home") == nil {
|
||||||
|
t.Error("expected command to have picoclaw-home flag")
|
||||||
|
}
|
||||||
|
}
|
||||||
51
cmd/picoclaw/internal/onboard/command_test.go
Normal file
51
cmd/picoclaw/internal/onboard/command_test.go
Normal file
|
|
@ -0,0 +1,51 @@
|
||||||
|
package onboard
|
||||||
|
|
||||||
|
import "testing"
|
||||||
|
|
||||||
|
func TestNewOnboardCommand(t *testing.T) {
|
||||||
|
cmd := NewOnboardCommand()
|
||||||
|
|
||||||
|
if cmd == nil {
|
||||||
|
t.Fatalf("expected non-nil command")
|
||||||
|
}
|
||||||
|
|
||||||
|
if cmd.Use != "onboard" {
|
||||||
|
t.Errorf("expected command name 'onboard', got %q", cmd.Use)
|
||||||
|
}
|
||||||
|
|
||||||
|
if cmd.Short != "Initialize picoclaw configuration and workspace" {
|
||||||
|
t.Errorf("expected command short description, got %q", cmd.Short)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(cmd.Aliases) != 1 {
|
||||||
|
t.Errorf("expected command to have 1 alias, got %d", len(cmd.Aliases))
|
||||||
|
}
|
||||||
|
|
||||||
|
if !cmd.HasAlias("o") {
|
||||||
|
t.Errorf("expected command to have alias 'o'")
|
||||||
|
}
|
||||||
|
|
||||||
|
if cmd.Run == nil {
|
||||||
|
t.Error("expected command to have non-nil Run()")
|
||||||
|
}
|
||||||
|
|
||||||
|
if cmd.RunE != nil {
|
||||||
|
t.Error("expected command to have nil RunE()")
|
||||||
|
}
|
||||||
|
|
||||||
|
if cmd.PersistentPreRun != nil {
|
||||||
|
t.Error("expected command to have nil PersistentPreRun()")
|
||||||
|
}
|
||||||
|
|
||||||
|
if cmd.PersistentPostRun != nil {
|
||||||
|
t.Error("expected command to have nil PersistentPostRun()")
|
||||||
|
}
|
||||||
|
|
||||||
|
if cmd.HasFlags() {
|
||||||
|
t.Error("expected command to have no flags")
|
||||||
|
}
|
||||||
|
|
||||||
|
if cmd.HasSubCommands() {
|
||||||
|
t.Error("expected command to have no subcommands")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -12,7 +12,7 @@ import (
|
||||||
func NewSkillsCommand() *cobra.Command {
|
func NewSkillsCommand() *cobra.Command {
|
||||||
cmd := &cobra.Command{
|
cmd := &cobra.Command{
|
||||||
Use: "skills",
|
Use: "skills",
|
||||||
Short: "Manage skills (install, list, remove)",
|
Short: "Manage skills",
|
||||||
RunE: func(cmd *cobra.Command, _ []string) error {
|
RunE: func(cmd *cobra.Command, _ []string) error {
|
||||||
return cmd.Help()
|
return cmd.Help()
|
||||||
},
|
},
|
||||||
|
|
|
||||||
39
cmd/picoclaw/internal/skills/install_test.go
Normal file
39
cmd/picoclaw/internal/skills/install_test.go
Normal file
|
|
@ -0,0 +1,39 @@
|
||||||
|
package skills
|
||||||
|
|
||||||
|
import "testing"
|
||||||
|
|
||||||
|
func TestNewInstallSubcommand(t *testing.T) {
|
||||||
|
cmd := newInstallCommand(nil)
|
||||||
|
|
||||||
|
if cmd == nil {
|
||||||
|
t.Fatalf("expected non-nil command")
|
||||||
|
}
|
||||||
|
|
||||||
|
if cmd.Use != "install" {
|
||||||
|
t.Errorf("expected command name 'install', got %q", cmd.Use)
|
||||||
|
}
|
||||||
|
|
||||||
|
if cmd.Short != "Install skill from GitHub" {
|
||||||
|
t.Errorf("expected command short description, got %q", cmd.Short)
|
||||||
|
}
|
||||||
|
|
||||||
|
if cmd.Run == nil {
|
||||||
|
t.Error("expected command to have non-nil Run()")
|
||||||
|
}
|
||||||
|
|
||||||
|
if !cmd.HasExample() {
|
||||||
|
t.Error("expected command to have example")
|
||||||
|
}
|
||||||
|
|
||||||
|
if cmd.HasSubCommands() {
|
||||||
|
t.Error("expected command to have no subcommands")
|
||||||
|
}
|
||||||
|
|
||||||
|
if cmd.HasFlags() {
|
||||||
|
t.Error("expected command to have no flags")
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(cmd.Aliases) > 0 {
|
||||||
|
t.Errorf("expected command to have no aliases, got %d", len(cmd.Aliases))
|
||||||
|
}
|
||||||
|
}
|
||||||
39
cmd/picoclaw/internal/skills/installbuiltin_test.go
Normal file
39
cmd/picoclaw/internal/skills/installbuiltin_test.go
Normal file
|
|
@ -0,0 +1,39 @@
|
||||||
|
package skills
|
||||||
|
|
||||||
|
import "testing"
|
||||||
|
|
||||||
|
func TestNewInstallbuiltinSubcommand(t *testing.T) {
|
||||||
|
cmd := newInstallBuiltinCommand("")
|
||||||
|
|
||||||
|
if cmd == nil {
|
||||||
|
t.Fatalf("expected non-nil command")
|
||||||
|
}
|
||||||
|
|
||||||
|
if cmd.Use != "install-builtin" {
|
||||||
|
t.Errorf("expected command name 'install-builtin', got %q", cmd.Use)
|
||||||
|
}
|
||||||
|
|
||||||
|
if cmd.Short != "Install all builtin skills to workspace" {
|
||||||
|
t.Errorf("expected command short description, got %q", cmd.Short)
|
||||||
|
}
|
||||||
|
|
||||||
|
if cmd.Run == nil {
|
||||||
|
t.Error("expected command to have non-nil Run()")
|
||||||
|
}
|
||||||
|
|
||||||
|
if !cmd.HasExample() {
|
||||||
|
t.Error("expected command to have example")
|
||||||
|
}
|
||||||
|
|
||||||
|
if cmd.HasSubCommands() {
|
||||||
|
t.Error("expected command to have no subcommands")
|
||||||
|
}
|
||||||
|
|
||||||
|
if cmd.HasFlags() {
|
||||||
|
t.Error("expected command to have no flags")
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(cmd.Aliases) > 0 {
|
||||||
|
t.Errorf("expected command to have no aliases, got %d", len(cmd.Aliases))
|
||||||
|
}
|
||||||
|
}
|
||||||
39
cmd/picoclaw/internal/skills/list_test.go
Normal file
39
cmd/picoclaw/internal/skills/list_test.go
Normal file
|
|
@ -0,0 +1,39 @@
|
||||||
|
package skills
|
||||||
|
|
||||||
|
import "testing"
|
||||||
|
|
||||||
|
func TestNewListSubcommand(t *testing.T) {
|
||||||
|
cmd := newListCommand(nil)
|
||||||
|
|
||||||
|
if cmd == nil {
|
||||||
|
t.Fatalf("expected non-nil command")
|
||||||
|
}
|
||||||
|
|
||||||
|
if cmd.Use != "list" {
|
||||||
|
t.Errorf("expected command name 'list', got %q", cmd.Use)
|
||||||
|
}
|
||||||
|
|
||||||
|
if cmd.Short != "List installed skills" {
|
||||||
|
t.Errorf("expected command short description, got %q", cmd.Short)
|
||||||
|
}
|
||||||
|
|
||||||
|
if cmd.Run == nil {
|
||||||
|
t.Error("expected command to have non-nil Run()")
|
||||||
|
}
|
||||||
|
|
||||||
|
if !cmd.HasExample() {
|
||||||
|
t.Error("expected command to have example")
|
||||||
|
}
|
||||||
|
|
||||||
|
if cmd.HasSubCommands() {
|
||||||
|
t.Error("expected command to have no subcommands")
|
||||||
|
}
|
||||||
|
|
||||||
|
if cmd.HasFlags() {
|
||||||
|
t.Error("expected command to have no flags")
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(cmd.Aliases) > 0 {
|
||||||
|
t.Errorf("expected command to have no aliases, got %d", len(cmd.Aliases))
|
||||||
|
}
|
||||||
|
}
|
||||||
39
cmd/picoclaw/internal/skills/listbuiltin_test.go
Normal file
39
cmd/picoclaw/internal/skills/listbuiltin_test.go
Normal file
|
|
@ -0,0 +1,39 @@
|
||||||
|
package skills
|
||||||
|
|
||||||
|
import "testing"
|
||||||
|
|
||||||
|
func TestNewListbuiltinSubcommand(t *testing.T) {
|
||||||
|
cmd := newListBuiltinCommand()
|
||||||
|
|
||||||
|
if cmd == nil {
|
||||||
|
t.Fatalf("expected non-nil command")
|
||||||
|
}
|
||||||
|
|
||||||
|
if cmd.Use != "list-builtin" {
|
||||||
|
t.Errorf("expected command name 'list-builtin', got %q", cmd.Use)
|
||||||
|
}
|
||||||
|
|
||||||
|
if cmd.Short != "List available builtin skills" {
|
||||||
|
t.Errorf("expected command short description, got %q", cmd.Short)
|
||||||
|
}
|
||||||
|
|
||||||
|
if cmd.Run == nil {
|
||||||
|
t.Error("expected command to have non-nil Run()")
|
||||||
|
}
|
||||||
|
|
||||||
|
if !cmd.HasExample() {
|
||||||
|
t.Error("expected command to have example")
|
||||||
|
}
|
||||||
|
|
||||||
|
if cmd.HasSubCommands() {
|
||||||
|
t.Error("expected command to have no subcommands")
|
||||||
|
}
|
||||||
|
|
||||||
|
if cmd.HasFlags() {
|
||||||
|
t.Error("expected command to have no flags")
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(cmd.Aliases) > 0 {
|
||||||
|
t.Errorf("expected command to have no aliases, got %d", len(cmd.Aliases))
|
||||||
|
}
|
||||||
|
}
|
||||||
47
cmd/picoclaw/internal/skills/remove_test.go
Normal file
47
cmd/picoclaw/internal/skills/remove_test.go
Normal file
|
|
@ -0,0 +1,47 @@
|
||||||
|
package skills
|
||||||
|
|
||||||
|
import "testing"
|
||||||
|
|
||||||
|
func TestNewRemoveSubcommand(t *testing.T) {
|
||||||
|
cmd := newRemoveCommand(nil)
|
||||||
|
|
||||||
|
if cmd == nil {
|
||||||
|
t.Fatalf("expected non-nil command")
|
||||||
|
}
|
||||||
|
|
||||||
|
if cmd.Use != "remove" {
|
||||||
|
t.Errorf("expected command name 'remove', got %q", cmd.Use)
|
||||||
|
}
|
||||||
|
|
||||||
|
if cmd.Short != "Remove installed skill" {
|
||||||
|
t.Errorf("expected command short description, got %q", cmd.Short)
|
||||||
|
}
|
||||||
|
|
||||||
|
if cmd.Run == nil {
|
||||||
|
t.Error("expected command to have non-nil Run()")
|
||||||
|
}
|
||||||
|
|
||||||
|
if !cmd.HasExample() {
|
||||||
|
t.Error("expected command to have example")
|
||||||
|
}
|
||||||
|
|
||||||
|
if cmd.HasSubCommands() {
|
||||||
|
t.Error("expected command to have no subcommands")
|
||||||
|
}
|
||||||
|
|
||||||
|
if cmd.HasFlags() {
|
||||||
|
t.Error("expected command to have no flags")
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(cmd.Aliases) != 2 {
|
||||||
|
t.Errorf("expected command to have 2 aliases, got %d", len(cmd.Aliases))
|
||||||
|
}
|
||||||
|
|
||||||
|
if !cmd.HasAlias("rm") {
|
||||||
|
t.Errorf("expected command to have alias 'rm'")
|
||||||
|
}
|
||||||
|
|
||||||
|
if !cmd.HasAlias("uninstall") {
|
||||||
|
t.Errorf("expected command to have alias 'uninstall'")
|
||||||
|
}
|
||||||
|
}
|
||||||
35
cmd/picoclaw/internal/skills/search_test.go
Normal file
35
cmd/picoclaw/internal/skills/search_test.go
Normal file
|
|
@ -0,0 +1,35 @@
|
||||||
|
package skills
|
||||||
|
|
||||||
|
import "testing"
|
||||||
|
|
||||||
|
func TestNewSearchSubcommand(t *testing.T) {
|
||||||
|
cmd := newSearchCommand(nil)
|
||||||
|
|
||||||
|
if cmd == nil {
|
||||||
|
t.Fatalf("expected non-nil command")
|
||||||
|
}
|
||||||
|
|
||||||
|
if cmd.Use != "search" {
|
||||||
|
t.Errorf("expected command name 'search', got %q", cmd.Use)
|
||||||
|
}
|
||||||
|
|
||||||
|
if cmd.Short != "Search available skills" {
|
||||||
|
t.Errorf("expected command short description, got %q", cmd.Short)
|
||||||
|
}
|
||||||
|
|
||||||
|
if cmd.Run == nil {
|
||||||
|
t.Error("expected command to have non-nil Run()")
|
||||||
|
}
|
||||||
|
|
||||||
|
if cmd.HasSubCommands() {
|
||||||
|
t.Error("expected command to have no subcommands")
|
||||||
|
}
|
||||||
|
|
||||||
|
if cmd.HasFlags() {
|
||||||
|
t.Error("expected command to have no flags")
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(cmd.Aliases) > 0 {
|
||||||
|
t.Errorf("expected command to have no aliases, got %d", len(cmd.Aliases))
|
||||||
|
}
|
||||||
|
}
|
||||||
39
cmd/picoclaw/internal/skills/show_test.go
Normal file
39
cmd/picoclaw/internal/skills/show_test.go
Normal file
|
|
@ -0,0 +1,39 @@
|
||||||
|
package skills
|
||||||
|
|
||||||
|
import "testing"
|
||||||
|
|
||||||
|
func TestNewShowSubcommand(t *testing.T) {
|
||||||
|
cmd := newShowCommand(nil)
|
||||||
|
|
||||||
|
if cmd == nil {
|
||||||
|
t.Fatalf("expected non-nil command")
|
||||||
|
}
|
||||||
|
|
||||||
|
if cmd.Use != "show" {
|
||||||
|
t.Errorf("expected command name 'show', got %q", cmd.Use)
|
||||||
|
}
|
||||||
|
|
||||||
|
if cmd.Short != "Show skill details" {
|
||||||
|
t.Errorf("expected command short description, got %q", cmd.Short)
|
||||||
|
}
|
||||||
|
|
||||||
|
if cmd.Run == nil {
|
||||||
|
t.Error("expected command to have non-nil Run()")
|
||||||
|
}
|
||||||
|
|
||||||
|
if !cmd.HasExample() {
|
||||||
|
t.Error("expected command to have example")
|
||||||
|
}
|
||||||
|
|
||||||
|
if cmd.HasSubCommands() {
|
||||||
|
t.Error("expected command to have no subcommands")
|
||||||
|
}
|
||||||
|
|
||||||
|
if cmd.HasFlags() {
|
||||||
|
t.Error("expected command to have no flags")
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(cmd.Aliases) > 0 {
|
||||||
|
t.Errorf("expected command to have no aliases, got %d", len(cmd.Aliases))
|
||||||
|
}
|
||||||
|
}
|
||||||
47
cmd/picoclaw/internal/skills/skills_test.go
Normal file
47
cmd/picoclaw/internal/skills/skills_test.go
Normal file
|
|
@ -0,0 +1,47 @@
|
||||||
|
package skills
|
||||||
|
|
||||||
|
import "testing"
|
||||||
|
|
||||||
|
func TestNewSkillsCommand(t *testing.T) {
|
||||||
|
cmd := NewSkillsCommand()
|
||||||
|
|
||||||
|
if cmd == nil {
|
||||||
|
t.Fatalf("expected non-nil command")
|
||||||
|
}
|
||||||
|
|
||||||
|
if cmd.Use != "skills" {
|
||||||
|
t.Errorf("expected command name 'skills', got %q", cmd.Use)
|
||||||
|
}
|
||||||
|
|
||||||
|
if cmd.Short != "Manage skills" {
|
||||||
|
t.Errorf("expected command short description, got %q", cmd.Short)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(cmd.Aliases) > 0 {
|
||||||
|
t.Errorf("expected command to have no aliases, got %d", len(cmd.Aliases))
|
||||||
|
}
|
||||||
|
|
||||||
|
if cmd.HasFlags() {
|
||||||
|
t.Error("expected command to have no flags")
|
||||||
|
}
|
||||||
|
|
||||||
|
if cmd.Run != nil {
|
||||||
|
t.Error("expected command to have nil Run()")
|
||||||
|
}
|
||||||
|
|
||||||
|
if cmd.RunE == nil {
|
||||||
|
t.Error("expected command to have non-nil RunE()")
|
||||||
|
}
|
||||||
|
|
||||||
|
if cmd.PersistentPreRunE == nil {
|
||||||
|
t.Error("expected command to have persistent pre-run hook")
|
||||||
|
}
|
||||||
|
|
||||||
|
if cmd.PersistentPreRun != nil {
|
||||||
|
t.Error("expected command to have nil PersistentPreRun()")
|
||||||
|
}
|
||||||
|
|
||||||
|
if cmd.PersistentPostRun != nil {
|
||||||
|
t.Error("expected command to have nil PersistentPostRun()")
|
||||||
|
}
|
||||||
|
}
|
||||||
47
cmd/picoclaw/internal/status/command_test.go
Normal file
47
cmd/picoclaw/internal/status/command_test.go
Normal file
|
|
@ -0,0 +1,47 @@
|
||||||
|
package status
|
||||||
|
|
||||||
|
import "testing"
|
||||||
|
|
||||||
|
func TestNewStatusCommand(t *testing.T) {
|
||||||
|
cmd := NewStatusCommand()
|
||||||
|
|
||||||
|
if cmd == nil {
|
||||||
|
t.Fatalf("expected non-nil command")
|
||||||
|
}
|
||||||
|
|
||||||
|
if cmd.Use != "status" {
|
||||||
|
t.Errorf("expected command name 'status', got %q", cmd.Use)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(cmd.Aliases) != 1 {
|
||||||
|
t.Errorf("expected command to have 1 alias, got %d", len(cmd.Aliases))
|
||||||
|
}
|
||||||
|
|
||||||
|
if !cmd.HasAlias("s") {
|
||||||
|
t.Errorf("expected command to have alias 's'")
|
||||||
|
}
|
||||||
|
|
||||||
|
if cmd.Short != "Show picoclaw status" {
|
||||||
|
t.Errorf("expected command short description, got %q", cmd.Short)
|
||||||
|
}
|
||||||
|
|
||||||
|
if cmd.HasSubCommands() {
|
||||||
|
t.Error("expected command to have no subcommands")
|
||||||
|
}
|
||||||
|
|
||||||
|
if cmd.Run == nil {
|
||||||
|
t.Error("expected command to have non-nil Run()")
|
||||||
|
}
|
||||||
|
|
||||||
|
if cmd.RunE != nil {
|
||||||
|
t.Error("expected command to have nil RunE()")
|
||||||
|
}
|
||||||
|
|
||||||
|
if cmd.PersistentPreRun != nil {
|
||||||
|
t.Error("expected command to have nil PersistentPreRun()")
|
||||||
|
}
|
||||||
|
|
||||||
|
if cmd.PersistentPostRun != nil {
|
||||||
|
t.Error("expected command to have nil PersistentPostRun()")
|
||||||
|
}
|
||||||
|
}
|
||||||
51
cmd/picoclaw/internal/version/command_test.go
Normal file
51
cmd/picoclaw/internal/version/command_test.go
Normal file
|
|
@ -0,0 +1,51 @@
|
||||||
|
package version
|
||||||
|
|
||||||
|
import "testing"
|
||||||
|
|
||||||
|
func TestNewVersionCommand(t *testing.T) {
|
||||||
|
cmd := NewVersionCommand()
|
||||||
|
|
||||||
|
if cmd == nil {
|
||||||
|
t.Fatalf("expected non-nil command")
|
||||||
|
}
|
||||||
|
|
||||||
|
if cmd.Use != "version" {
|
||||||
|
t.Errorf("expected command name 'version', got %q", cmd.Use)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(cmd.Aliases) != 1 {
|
||||||
|
t.Errorf("expected command to have 1 alias, got %d", len(cmd.Aliases))
|
||||||
|
}
|
||||||
|
|
||||||
|
if !cmd.HasAlias("v") {
|
||||||
|
t.Errorf("expected command to have alias 'v'")
|
||||||
|
}
|
||||||
|
|
||||||
|
if cmd.HasFlags() {
|
||||||
|
t.Error("expected command to have no flags")
|
||||||
|
}
|
||||||
|
|
||||||
|
if cmd.Short != "Show version information" {
|
||||||
|
t.Errorf("expected command short description, got %q", cmd.Short)
|
||||||
|
}
|
||||||
|
|
||||||
|
if cmd.HasSubCommands() {
|
||||||
|
t.Error("expected command to have no subcommands")
|
||||||
|
}
|
||||||
|
|
||||||
|
if cmd.Run == nil {
|
||||||
|
t.Error("expected command to have non-nil Run()")
|
||||||
|
}
|
||||||
|
|
||||||
|
if cmd.RunE != nil {
|
||||||
|
t.Error("expected command to have nil RunE()")
|
||||||
|
}
|
||||||
|
|
||||||
|
if cmd.PersistentPreRun != nil {
|
||||||
|
t.Error("expected command to have nil PersistentPreRun()")
|
||||||
|
}
|
||||||
|
|
||||||
|
if cmd.PersistentPostRun != nil {
|
||||||
|
t.Error("expected command to have nil PersistentPostRun()")
|
||||||
|
}
|
||||||
|
}
|
||||||
71
cmd/picoclaw/main_test.go
Normal file
71
cmd/picoclaw/main_test.go
Normal file
|
|
@ -0,0 +1,71 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestNewPicoclawCommand(t *testing.T) {
|
||||||
|
cmd := NewPicoclawCommand()
|
||||||
|
|
||||||
|
if cmd == nil {
|
||||||
|
t.Fatalf("expected non-nil command")
|
||||||
|
}
|
||||||
|
|
||||||
|
if cmd.Use != "picoclaw" {
|
||||||
|
t.Errorf("expected command name 'picoclaw', got %q", cmd.Use)
|
||||||
|
}
|
||||||
|
|
||||||
|
if cmd.Short != "picoclaw — Personal AI Assistant" {
|
||||||
|
t.Errorf("expected command short description, got %q", cmd.Short)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !cmd.HasSubCommands() {
|
||||||
|
t.Error("expected command to have subcommands")
|
||||||
|
}
|
||||||
|
|
||||||
|
if !cmd.HasAvailableSubCommands() {
|
||||||
|
t.Error("expected command to have available subcommands")
|
||||||
|
}
|
||||||
|
|
||||||
|
if cmd.HasFlags() {
|
||||||
|
t.Error("expected command to have no flags")
|
||||||
|
}
|
||||||
|
|
||||||
|
if cmd.Run != nil {
|
||||||
|
t.Error("expected command to have nil Run()")
|
||||||
|
}
|
||||||
|
|
||||||
|
if cmd.RunE != nil {
|
||||||
|
t.Error("expected command to have nil RunE()")
|
||||||
|
}
|
||||||
|
|
||||||
|
if cmd.PersistentPreRun != nil {
|
||||||
|
t.Error("expected command to have nil PersistentPreRun()")
|
||||||
|
}
|
||||||
|
|
||||||
|
if cmd.PersistentPostRun != nil {
|
||||||
|
t.Error("expected command to have nil PersistentPostRun()")
|
||||||
|
}
|
||||||
|
|
||||||
|
allowedCommands := map[string]struct{}{
|
||||||
|
"agent": {},
|
||||||
|
"auth": {},
|
||||||
|
"cron": {},
|
||||||
|
"gateway": {},
|
||||||
|
"migrate": {},
|
||||||
|
"onboard": {},
|
||||||
|
"skills": {},
|
||||||
|
"status": {},
|
||||||
|
"version": {},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, subcmd := range cmd.Commands() {
|
||||||
|
if _, found := allowedCommands[subcmd.Name()]; !found {
|
||||||
|
t.Errorf("unexpected subcommand %q", subcmd.Name())
|
||||||
|
}
|
||||||
|
|
||||||
|
if cmd.Hidden {
|
||||||
|
t.Errorf("expected subcommand %q to be visible", subcmd.Name())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Add table
Reference in a new issue