crabbox/internal/cli/init_test.go
2026-05-04 23:32:22 +01:00

171 lines
4.9 KiB
Go

package cli
import (
"bytes"
"context"
"os"
"path/filepath"
"strings"
"testing"
)
func TestInitProjectWritesExpectedFiles(t *testing.T) {
dir := t.TempDir()
oldwd, err := os.Getwd()
if err != nil {
t.Fatal(err)
}
if err := os.Chdir(dir); err != nil {
t.Fatal(err)
}
t.Cleanup(func() {
_ = os.Chdir(oldwd)
})
app := App{Stdout: &bytes.Buffer{}, Stderr: &bytes.Buffer{}}
if err := app.Run(context.Background(), []string{"init"}); err != nil {
t.Fatalf("init error: %v", err)
}
for _, path := range []string{
".crabbox.yaml",
".github/workflows/crabbox.yml",
".agents/skills/crabbox/SKILL.md",
} {
if _, err := os.Stat(filepath.Join(dir, path)); err != nil {
t.Fatalf("expected %s: %v", path, err)
}
}
data, err := os.ReadFile(filepath.Join(dir, ".agents/skills/crabbox/SKILL.md"))
if err != nil {
t.Fatal(err)
}
if !strings.Contains(string(data), "crabbox warmup") {
t.Fatalf("skill missing warmup instructions: %s", data)
}
workflow, err := os.ReadFile(filepath.Join(dir, ".github/workflows/crabbox.yml"))
if err != nil {
t.Fatal(err)
}
for _, want := range []string{
"crabbox_job:",
"ENV_FILE=${env_file}",
"SERVICES_FILE=${services_file}",
"RUNNER_TOOL_CACHE",
} {
if !strings.Contains(string(workflow), want) {
t.Fatalf("workflow missing %q:\n%s", want, workflow)
}
}
config, err := os.ReadFile(filepath.Join(dir, ".crabbox.yaml"))
if err != nil {
t.Fatal(err)
}
if !strings.Contains(string(config), "job: hydrate") {
t.Fatalf("config missing actions job:\n%s", config)
}
if err := app.Run(context.Background(), []string{"init"}); err == nil {
t.Fatal("second init without --force succeeded")
}
}
func TestSubcommandHelpExitsZero(t *testing.T) {
var stderr bytes.Buffer
app := App{Stdout: &bytes.Buffer{}, Stderr: &stderr}
err := app.Run(context.Background(), []string{"init", "--help"})
var exitErr ExitError
if !AsExitError(err, &exitErr) || exitErr.Code != 0 {
t.Fatalf("init --help error=%v, want exit 0", err)
}
if !strings.Contains(stderr.String(), "Usage of init") {
t.Fatalf("init --help output missing usage: %s", stderr.String())
}
}
func TestPassthroughCommandHelpExitsBeforeExecution(t *testing.T) {
for _, command := range []string{"warmup", "run", "status", "ssh", "vnc", "webvnc", "screenshot", "inspect", "stop"} {
t.Run(command, func(t *testing.T) {
var stderr bytes.Buffer
app := App{Stdout: &bytes.Buffer{}, Stderr: &stderr}
err := app.Run(context.Background(), []string{command, "--help"})
var exitErr ExitError
if !AsExitError(err, &exitErr) || exitErr.Code != 0 {
t.Fatalf("%s --help error=%v, want exit 0", command, err)
}
if !strings.Contains(stderr.String(), "Usage") {
t.Fatalf("%s --help output missing usage: %s", command, stderr.String())
}
})
}
}
func TestGroupedCommandHelpExitsZero(t *testing.T) {
for _, command := range []string{"actions", "admin", "cache", "config", "desktop", "pool", "machine"} {
t.Run(command, func(t *testing.T) {
for _, args := range [][]string{
{command, "--help"},
{command, "help"},
{command},
} {
var stdout bytes.Buffer
app := App{Stdout: &stdout, Stderr: &bytes.Buffer{}}
err := app.Run(context.Background(), args)
if err != nil {
t.Fatalf("%v error=%v, want nil", args, err)
}
if !strings.Contains(stdout.String(), "Usage:") {
t.Fatalf("%v output missing usage: %s", args, stdout.String())
}
}
})
}
}
func TestHelpSubcommandRoutesToCommandHelp(t *testing.T) {
var stderr bytes.Buffer
app := App{Stdout: &bytes.Buffer{}, Stderr: &stderr}
err := app.Run(context.Background(), []string{"help", "run"})
var exitErr ExitError
if !AsExitError(err, &exitErr) || exitErr.Code != 0 {
t.Fatalf("help run error=%v, want exit 0", err)
}
if !strings.Contains(stderr.String(), "Usage of run") {
t.Fatalf("help run output missing usage: %s", stderr.String())
}
}
func TestTopLevelHelpIsWorkflowFirst(t *testing.T) {
var stdout bytes.Buffer
app := App{Stdout: &stdout, Stderr: &bytes.Buffer{}}
if err := app.Run(context.Background(), []string{"help"}); err != nil {
t.Fatalf("help error: %v", err)
}
for _, want := range []string{
"Start Here:",
"Commands:",
"Common Flows:",
"crabbox run --id blue-lobster -- pnpm test:changed",
"Aliases:",
} {
if !strings.Contains(stdout.String(), want) {
t.Fatalf("help output missing %q:\n%s", want, stdout.String())
}
}
}
func TestKongRouterPreservesVersionAndUsageExitCodes(t *testing.T) {
var stdout bytes.Buffer
app := App{Stdout: &stdout, Stderr: &bytes.Buffer{}}
if err := app.Run(context.Background(), []string{"--version"}); err != nil {
t.Fatalf("--version error: %v", err)
}
if strings.TrimSpace(stdout.String()) != version {
t.Fatalf("--version output=%q, want %q", stdout.String(), version)
}
err := app.Run(context.Background(), []string{"nope"})
var exitErr ExitError
if !AsExitError(err, &exitErr) || exitErr.Code != 2 {
t.Fatalf("unknown command error=%v, want exit 2", err)
}
}