package main import ( "bytes" "encoding/json" "net/http" "net/http/httptest" "os" "os/exec" "path/filepath" "strings" "testing" ) // mockServer returns an httptest.Server that handles molecule-cli API calls. // It serves responses under basePath so tests can hit . func mockServer(t *testing.T, basePath string) *httptest.Server { mux := http.NewServeMux() server := httptest.NewServer(mux) // --- Workspaces --- workspaces := []map[string]interface{}{ { "id": "ws-001", "name": "test-workspace", "status": "online", "role": "researcher", "runtime": "claude-code", "created_at": "2026-04-01T12:00:00Z", "tier": 2, }, { "id": "ws-002", "name": "prod-workspace", "status": "online", "role": "pm", "tier": 3, }, } mux.HandleFunc(basePath+"/workspaces", func(w http.ResponseWriter, r *http.Request) { switch r.Method { case http.MethodGet: w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(workspaces) case http.MethodPost: var req map[string]interface{} json.NewDecoder(r.Body).Decode(&req) resp := map[string]interface{}{ "id": "ws-new", "name": req["name"], "status": "creating", "created_at": "2026-04-21T00:00:00Z", } w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusCreated) json.NewEncoder(w).Encode(resp) default: http.Error(w, "method not allowed", http.StatusMethodNotAllowed) } }) mux.HandleFunc(basePath+"/workspaces/ws-001", func(w http.ResponseWriter, r *http.Request) { switch r.Method { case http.MethodGet: w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(workspaces[0]) case http.MethodDelete: // CLI may send ?confirm=true query param w.WriteHeader(http.StatusNoContent) default: http.Error(w, "method not allowed", http.StatusMethodNotAllowed) } }) mux.HandleFunc(basePath+"/workspaces/ws-missing", func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") http.Error(w, `{"error":"workspace not found"}`, http.StatusNotFound) }) mux.HandleFunc(basePath+"/workspaces/ws-001/restart", func(w http.ResponseWriter, r *http.Request) { if r.Method != http.MethodPost { http.Error(w, "method not allowed", http.StatusMethodNotAllowed) return } w.WriteHeader(http.StatusNoContent) }) mux.HandleFunc(basePath+"/workspaces/ws-001/delete", func(w http.ResponseWriter, r *http.Request) { if r.Method != http.MethodDelete { http.Error(w, "method not allowed", http.StatusMethodNotAllowed) return } w.WriteHeader(http.StatusNoContent) }) mux.HandleFunc(basePath+"/workspaces/ws-001/agents", func(w http.ResponseWriter, r *http.Request) { if r.Method != http.MethodGet { http.Error(w, "method not allowed", http.StatusMethodNotAllowed) return } agents := []map[string]interface{}{ {"id": "ag-001", "name": "researcher-agent", "workspace_id": "ws-001", "status": "online", "model": "claude-opus-4"}, } w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(agents) }) mux.HandleFunc(basePath+"/workspaces/ws-001/delegate", func(w http.ResponseWriter, r *http.Request) { if r.Method != http.MethodPost { http.Error(w, "method not allowed", http.StatusMethodNotAllowed) return } resp := map[string]interface{}{ "delegation_id": "del-001", "status": "queued", } w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusAccepted) json.NewEncoder(w).Encode(resp) }) // --- Agents --- agents := []map[string]interface{}{ {"id": "ag-001", "name": "researcher-agent", "workspace_id": "ws-001", "status": "online", "model": "claude-opus-4"}, {"id": "ag-002", "name": "pm-agent", "workspace_id": "ws-002", "status": "online", "model": "claude-sonnet-4"}, } mux.HandleFunc(basePath+"/agents", func(w http.ResponseWriter, r *http.Request) { if r.Method != http.MethodGet { http.Error(w, "method not allowed", http.StatusMethodNotAllowed) return } w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(agents) }) mux.HandleFunc(basePath+"/agents/ag-001", func(w http.ResponseWriter, r *http.Request) { if r.Method != http.MethodGet { http.Error(w, "method not allowed", http.StatusMethodNotAllowed) return } w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(agents[0]) }) mux.HandleFunc(basePath+"/agents/ag-missing", func(w http.ResponseWriter, r *http.Request) { http.Error(w, `{"error":"agent not found"}`, http.StatusNotFound) }) mux.HandleFunc(basePath+"/registry/ws-001/peers", func(w http.ResponseWriter, r *http.Request) { if r.Method != http.MethodGet { http.Error(w, "method not allowed", http.StatusMethodNotAllowed) return } peers := []map[string]interface{}{ {"id": "ws-002", "name": "prod-workspace", "workspace_id": "ws-002", "status": "online", "model": "claude-sonnet-4"}, } w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(peers) }) mux.HandleFunc(basePath+"/workspaces/ws-001/a2a", func(w http.ResponseWriter, r *http.Request) { if r.Method != http.MethodPost { http.Error(w, "method not allowed", http.StatusMethodNotAllowed) return } var req map[string]string json.NewDecoder(r.Body).Decode(&req) resp := map[string]interface{}{ "result": "Message delivered to researcher-agent: " + req["message"], } w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(resp) }) // --- Health --- mux.HandleFunc(basePath+"/health", func(w http.ResponseWriter, r *http.Request) { if r.Method != http.MethodGet { http.Error(w, "method not allowed", http.StatusMethodNotAllowed) return } resp := map[string]interface{}{ "status": "ok", "version": "1.2.3", "uptime": "42h", } w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(resp) }) return server } // repoRoot returns the repo root directory. // CI runs tests from the repo checkout dir, so cwd is the right anchor. func repoRoot() string { cwd, _ := os.Getwd() return cwd } // mol returns the path to the CLI binary, building it if needed. func mol(t *testing.T) string { root := repoRoot() exe := filepath.Join(t.TempDir(), "molecule") // Build from the module root (where go.mod lives). // "go build ." packages the whole module; ./cmd/molecule is unnecessary // and Go 1.25 may resolve it differently. cmd := exec.Command("go", "build", "-o", exe, ".") cmd.Dir = root out, err := cmd.CombinedOutput() if err != nil { t.Fatalf("go build: %v\n%s", err, out) } return exe } // TestMain exists so we can skip tests when go build fails outside of normal circumstances. // The real test logic is in the functions below. func TestMain(m *testing.M) { os.Exit(m.Run()) } func TestCLI_Help(t *testing.T) { tests := []struct { name string args []string stderr bool // expect no stderr output on success }{ {"root help", []string{"--help"}, false}, {"workspace help", []string{"workspace", "--help"}, false}, {"agent help", []string{"agent", "--help"}, false}, {"platform help", []string{"platform", "--help"}, false}, {"config help", []string{"config", "--help"}, false}, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { exe := mol(t) root := repoRoot() cmd := exec.Command(exe, tc.args...) var stdout, stderr bytes.Buffer cmd.Stdout = &stdout cmd.Stderr = &stderr cmd.Dir = root err := cmd.Run() if err != nil { t.Fatalf("molecule %v: %v\nstdout: %s\nstderr: %s", strings.Join(tc.args, " "), err, stdout.String(), stderr.String()) } if stderr.Len() > 0 && tc.stderr { t.Errorf("unexpected stderr:\n%s", stderr.String()) } out := stdout.String() if out == "" { t.Errorf("empty stdout for mol %v", strings.Join(tc.args, " ")) } }) } } func TestCLI_Version(t *testing.T) { exe := mol(t) root := repoRoot() cmd := exec.Command(exe, "--version") var stdout, stderr bytes.Buffer cmd.Stdout = &stdout cmd.Stderr = &stderr cmd.Dir = root err := cmd.Run() if err != nil { t.Fatalf("molecule --version: %v", err) } out := stdout.String() if !strings.Contains(out, "molecule") { t.Errorf("expected 'molecule' in version output, got: %s", out) } } func TestCLI_WorkspaceList(t *testing.T) { server := mockServer(t, "") defer server.Close() exe := mol(t) root := repoRoot() cmd := exec.Command(exe, "--api-url", server.URL, "workspace", "list") var stdout, stderr bytes.Buffer cmd.Stdout = &stdout cmd.Stderr = &stderr cmd.Dir = root err := cmd.Run() if err != nil { t.Fatalf("molecule workspace list: %v\nstderr: %s", err, stderr.String()) } out := stdout.String() if !strings.Contains(out, "test-workspace") { t.Errorf("expected 'test-workspace' in output, got:\n%s", out) } if !strings.Contains(out, "prod-workspace") { t.Errorf("expected 'prod-workspace' in output, got:\n%s", out) } } func TestCLI_WorkspaceList_JSON(t *testing.T) { server := mockServer(t, "") defer server.Close() exe := mol(t) root := repoRoot() cmd := exec.Command(exe, "--api-url", server.URL, "--output", "json", "workspace", "list") var stdout, stderr bytes.Buffer cmd.Stdout = &stdout cmd.Stderr = &stderr cmd.Dir = root err := cmd.Run() if err != nil { t.Fatalf("molecule workspace list --output json: %v\nstderr: %s", err, stderr.String()) } var out []map[string]interface{} if err := json.Unmarshal(stdout.Bytes(), &out); err != nil { t.Fatalf("non-JSON output: %s\nstderr: %s", stdout.String(), stderr.String()) } if len(out) != 2 { t.Errorf("expected 2 workspaces, got %d", len(out)) } } func TestCLI_WorkspaceInspect(t *testing.T) { server := mockServer(t, "") defer server.Close() exe := mol(t) root := repoRoot() cmd := exec.Command(exe, "--api-url", server.URL, "workspace", "inspect", "ws-001") var stdout, stderr bytes.Buffer cmd.Stdout = &stdout cmd.Stderr = &stderr cmd.Dir = root err := cmd.Run() if err != nil { t.Fatalf("molecule workspace inspect ws-001: %v\nstderr: %s", err, stderr.String()) } out := stdout.String() for _, want := range []string{"ws-001", "test-workspace", "online", "researcher"} { if !strings.Contains(out, want) { t.Errorf("expected %q in output, got:\n%s", want, out) } } } func TestCLI_WorkspaceInspect_NotFound(t *testing.T) { server := mockServer(t, "") defer server.Close() exe := mol(t) root := repoRoot() cmd := exec.Command(exe, "--api-url", server.URL, "workspace", "inspect", "ws-missing") var stderr bytes.Buffer cmd.Stderr = &stderr cmd.Dir = root err := cmd.Run() if err == nil { t.Fatalf("expected error for missing workspace, got none") } // Should exit with non-zero code exitErr, ok := err.(*exec.ExitError) if !ok { t.Fatalf("expected *exec.ExitError, got %T", err) } if exitErr.ExitCode() == 0 { t.Errorf("expected non-zero exit code for missing workspace, got 0") } } func TestCLI_WorkspaceCreate(t *testing.T) { server := mockServer(t, "") defer server.Close() exe := mol(t) root := repoRoot() cmd := exec.Command(exe, "--api-url", server.URL, "workspace", "create", "--name", "my-workspace") var stdout, stderr bytes.Buffer cmd.Stdout = &stdout cmd.Stderr = &stderr cmd.Dir = root err := cmd.Run() if err != nil { t.Fatalf("molecule workspace create: %v\nstderr: %s", err, stderr.String()) } out := stdout.String() if !strings.Contains(out, "my-workspace") { t.Errorf("expected 'my-workspace' in output, got:\n%s", out) } } func TestCLI_WorkspaceCreate_MissingName(t *testing.T) { server := mockServer(t, "") defer server.Close() exe := mol(t) root := repoRoot() cmd := exec.Command(exe, "--api-url", server.URL, "workspace", "create") var stderr bytes.Buffer cmd.Stderr = &stderr cmd.Dir = root err := cmd.Run() // Missing required flag should exit with non-zero code if err == nil { t.Fatalf("expected error for missing --name, got none") } exitErr, ok := err.(*exec.ExitError) if !ok { t.Fatalf("expected *exec.ExitError, got %T", err) } if exitErr.ExitCode() == 0 { t.Errorf("expected non-zero exit code for missing required flag, got 0") } } func TestCLI_WorkspaceDelete(t *testing.T) { server := mockServer(t, "") defer server.Close() exe := mol(t) root := repoRoot() cmd := exec.Command(exe, "--api-url", server.URL, "workspace", "delete", "ws-001") var stdout, stderr bytes.Buffer cmd.Stdout = &stdout cmd.Stderr = &stderr cmd.Dir = root err := cmd.Run() if err != nil { t.Fatalf("molecule workspace delete ws-001: %v\nstderr: %s", err, stderr.String()) } out := stdout.String() if !strings.Contains(out, "deleted") { t.Errorf("expected 'deleted' in output, got:\n%s", out) } } func TestCLI_WorkspaceRestart(t *testing.T) { server := mockServer(t, "") defer server.Close() exe := mol(t) root := repoRoot() cmd := exec.Command(exe, "--api-url", server.URL, "workspace", "restart", "ws-001") var stdout, stderr bytes.Buffer cmd.Stdout = &stdout cmd.Stderr = &stderr cmd.Dir = root err := cmd.Run() if err != nil { t.Fatalf("molecule workspace restart ws-001: %v\nstderr: %s", err, stderr.String()) } out := stdout.String() if !strings.Contains(out, "Restart") { t.Errorf("expected 'Restart' in output, got:\n%s", out) } } func TestCLI_WorkspaceAudit(t *testing.T) { server := mockServer(t, "") defer server.Close() exe := mol(t) root := repoRoot() cmd := exec.Command(exe, "--api-url", server.URL, "workspace", "audit") var stdout, stderr bytes.Buffer cmd.Stdout = &stdout cmd.Stderr = &stderr cmd.Dir = root err := cmd.Run() if err != nil { t.Fatalf("molecule workspace audit: %v\nstderr: %s", err, stderr.String()) } out := stdout.String() for _, want := range []string{"test-workspace", "prod-workspace", "researcher-agent"} { if !strings.Contains(out, want) { t.Errorf("expected %q in output, got:\n%s", want, out) } } } func TestCLI_WorkspaceDelegate(t *testing.T) { server := mockServer(t, "") defer server.Close() exe := mol(t) root := repoRoot() cmd := exec.Command(exe, "--api-url", server.URL, "workspace", "delegate", "ws-001", "ws-002", "do the thing") var stdout, stderr bytes.Buffer cmd.Stdout = &stdout cmd.Stderr = &stderr cmd.Dir = root err := cmd.Run() if err != nil { t.Fatalf("molecule workspace delegate: %v\nstderr: %s", err, stderr.String()) } out := stdout.String() if !strings.Contains(out, "Delegation") && !strings.Contains(out, "ws-002") { t.Errorf("expected delegation confirmation in output, got:\n%s", out) } } func TestCLI_AgentList(t *testing.T) { server := mockServer(t, "") defer server.Close() exe := mol(t) root := repoRoot() cmd := exec.Command(exe, "--api-url", server.URL, "agent", "list") var stdout, stderr bytes.Buffer cmd.Stdout = &stdout cmd.Stderr = &stderr cmd.Dir = root err := cmd.Run() if err != nil { t.Fatalf("molecule agent list: %v\nstderr: %s", err, stderr.String()) } out := stdout.String() for _, want := range []string{"researcher-agent", "pm-agent"} { if !strings.Contains(out, want) { t.Errorf("expected %q in output, got:\n%s", want, out) } } } func TestCLI_AgentList_WorkspaceFiltered(t *testing.T) { server := mockServer(t, "") defer server.Close() exe := mol(t) root := repoRoot() cmd := exec.Command(exe, "--api-url", server.URL, "agent", "list", "ws-001") var stdout, stderr bytes.Buffer cmd.Stdout = &stdout cmd.Stderr = &stderr cmd.Dir = root err := cmd.Run() if err != nil { t.Fatalf("molecule agent list ws-001: %v\nstderr: %s", err, stderr.String()) } out := stdout.String() if !strings.Contains(out, "researcher-agent") { t.Errorf("expected 'researcher-agent' in output, got:\n%s", out) } } func TestCLI_AgentInspect(t *testing.T) { server := mockServer(t, "") defer server.Close() exe := mol(t) root := repoRoot() cmd := exec.Command(exe, "--api-url", server.URL, "agent", "inspect", "ag-001") var stdout, stderr bytes.Buffer cmd.Stdout = &stdout cmd.Stderr = &stderr cmd.Dir = root err := cmd.Run() if err != nil { t.Fatalf("molecule agent inspect ag-001: %v\nstderr: %s", err, stderr.String()) } out := stdout.String() for _, want := range []string{"ag-001", "researcher-agent", "online"} { if !strings.Contains(out, want) { t.Errorf("expected %q in output, got:\n%s", want, out) } } } func TestCLI_AgentInspect_NotFound(t *testing.T) { server := mockServer(t, "") defer server.Close() exe := mol(t) root := repoRoot() cmd := exec.Command(exe, "--api-url", server.URL, "agent", "inspect", "ag-missing") var stderr bytes.Buffer cmd.Stderr = &stderr cmd.Dir = root err := cmd.Run() if err == nil { t.Fatalf("expected error for missing agent, got none") } } func TestCLI_AgentSend(t *testing.T) { server := mockServer(t, "") defer server.Close() exe := mol(t) root := repoRoot() cmd := exec.Command(exe, "--api-url", server.URL, "agent", "send", "ag-001", "hello world") var stdout, stderr bytes.Buffer cmd.Stdout = &stdout cmd.Stderr = &stderr cmd.Dir = root err := cmd.Run() if err != nil { t.Fatalf("molecule agent send: %v\nstderr: %s", err, stderr.String()) } out := stdout.String() if !strings.Contains(out, "hello world") && !strings.Contains(out, "delivered") { t.Errorf("expected delivery confirmation in output, got:\n%s", out) } } func TestCLI_AgentPeers(t *testing.T) { server := mockServer(t, "") defer server.Close() exe := mol(t) root := repoRoot() cmd := exec.Command(exe, "--api-url", server.URL, "agent", "peers", "ws-001") var stdout, stderr bytes.Buffer cmd.Stdout = &stdout cmd.Stderr = &stderr cmd.Dir = root err := cmd.Run() if err != nil { t.Fatalf("molecule agent peers ws-001: %v\nstderr: %s", err, stderr.String()) } out := stdout.String() if !strings.Contains(out, "prod-workspace") { t.Errorf("expected 'prod-workspace' in peers output, got:\n%s", out) } } func TestCLI_PlatformHealth(t *testing.T) { server := mockServer(t, "") defer server.Close() exe := mol(t) root := repoRoot() cmd := exec.Command(exe, "--api-url", server.URL, "platform", "health") var stdout, stderr bytes.Buffer cmd.Stdout = &stdout cmd.Stderr = &stderr cmd.Dir = root err := cmd.Run() if err != nil { t.Fatalf("molecule platform health: %v\nstderr: %s", err, stderr.String()) } out := stdout.String() if !strings.Contains(out, "ok") && !strings.Contains(out, "1.2.3") { t.Errorf("expected health info in output, got:\n%s", out) } } func TestCLI_PlatformAudit(t *testing.T) { server := mockServer(t, "") defer server.Close() exe := mol(t) root := repoRoot() cmd := exec.Command(exe, "--api-url", server.URL, "platform", "audit") var stdout, stderr bytes.Buffer cmd.Stdout = &stdout cmd.Stderr = &stderr cmd.Dir = root err := cmd.Run() if err != nil { t.Fatalf("molecule platform audit: %v\nstderr: %s", err, stderr.String()) } out := stdout.String() if !strings.Contains(out, "test-workspace") || !strings.Contains(out, "prod-workspace") { t.Errorf("expected workspaces in platform audit output, got:\n%s", out) } } func TestCLI_UnknownSubcommand(t *testing.T) { exe := mol(t) root := repoRoot() cmd := exec.Command(exe, "agen", "inspect") var stderr bytes.Buffer cmd.Stderr = &stderr cmd.Dir = root err := cmd.Run() if err == nil { t.Fatalf("expected error for unknown subcommand, got none") } exitErr, ok := err.(*exec.ExitError) if !ok { t.Fatalf("expected *exec.ExitError, got %T", err) } if exitErr.ExitCode() == 0 { t.Errorf("expected non-zero exit code for unknown subcommand, got 0") } } func TestCLI_MissingRequiredArg(t *testing.T) { server := mockServer(t, "") defer server.Close() exe := mol(t) root := repoRoot() cmd := exec.Command(exe, "--api-url", server.URL, "agent", "inspect") var stderr bytes.Buffer cmd.Stderr = &stderr cmd.Dir = root err := cmd.Run() if err == nil { t.Fatalf("expected error for missing required arg, got none") } exitErr, ok := err.(*exec.ExitError) if !ok { t.Fatalf("expected *exec.ExitError, got %T", err) } if exitErr.ExitCode() == 0 { t.Errorf("expected non-zero exit code for missing required arg, got 0") } } func TestCLI_ConfigInit(t *testing.T) { exe := mol(t) dir := t.TempDir() cmd := exec.Command(exe, "config", "init") cmd.Dir = dir var stdout, stderr bytes.Buffer cmd.Stdout = &stdout cmd.Stderr = &stderr err := cmd.Run() if err != nil { t.Fatalf("molecule config init: %v\nstderr: %s", err, stderr.String()) } f := filepath.Join(dir, "molecule.yaml") if _, err := os.Stat(f); err != nil { t.Errorf("molecule.yaml not scaffolded at %s", f) } } func TestCLI_ConfigList(t *testing.T) { server := mockServer(t, "") defer server.Close() exe := mol(t) root := repoRoot() cmd := exec.Command(exe, "--api-url", server.URL, "config", "list") var stdout, stderr bytes.Buffer cmd.Stdout = &stdout cmd.Stderr = &stderr cmd.Dir = root err := cmd.Run() if err != nil { t.Fatalf("molecule config list: %v\nstderr: %s", err, stderr.String()) } // Should at least show something without crashing out := stdout.String() if out == "" { t.Errorf("empty stdout for molecule config list") } } func TestCLI_Init(t *testing.T) { exe := mol(t) dir := t.TempDir() cmd := exec.Command(exe, "init") cmd.Dir = dir var stdout, stderr bytes.Buffer cmd.Stdout = &stdout cmd.Stderr = &stderr err := cmd.Run() if err != nil { t.Fatalf("molecule init: %v\nstderr: %s", err, stderr.String()) } f := filepath.Join(dir, "molecule.yaml") if _, err := os.Stat(f); err != nil { t.Errorf("molecule.yaml not scaffolded at %s", f) } out := stdout.String() if !strings.Contains(out, "Scaffolded") && !strings.Contains(out, "molecule.yaml") { t.Errorf("expected scaffolded confirmation in output, got:\n%s", out) } } func TestCLI_Init_AlreadyExists(t *testing.T) { exe := mol(t) dir := t.TempDir() // pre-create molecule.yaml os.WriteFile(filepath.Join(dir, "molecule.yaml"), []byte("x"), 0o644) cmd := exec.Command(exe, "init") cmd.Dir = dir var stderr bytes.Buffer cmd.Stderr = &stderr err := cmd.Run() if err == nil { t.Fatalf("expected error when molecule.yaml exists, got none") } exitErr, ok := err.(*exec.ExitError) if !ok { t.Fatalf("expected *exec.ExitError, got %T", err) } if exitErr.ExitCode() == 0 { t.Errorf("expected non-zero exit code when molecule.yaml exists, got 0") } } func TestCLI_Init_Force(t *testing.T) { exe := mol(t) dir := t.TempDir() // pre-create molecule.yaml existing := "# old config" os.WriteFile(filepath.Join(dir, "molecule.yaml"), []byte(existing), 0o644) cmd := exec.Command(exe, "init", "--force") cmd.Dir = dir var stdout, stderr bytes.Buffer cmd.Stdout = &stdout cmd.Stderr = &stderr err := cmd.Run() if err != nil { t.Fatalf("molecule init --force: %v\nstderr: %s", err, stderr.String()) } out := stdout.String() if !strings.Contains(out, "Replaced") { t.Errorf("expected 'Replaced' in output, got: %s", out) } } func TestCLI_Init_Force_Overwrites(t *testing.T) { exe := mol(t) dir := t.TempDir() // pre-create molecule.yaml os.WriteFile(filepath.Join(dir, "molecule.yaml"), []byte("old content"), 0o644) cmd := exec.Command(exe, "init", "--force") cmd.Dir = dir err := cmd.Run() if err != nil { t.Fatalf("molecule init --force: %v", err) } data, _ := os.ReadFile(filepath.Join(dir, "molecule.yaml")) if strings.Contains(string(data), "old content") { t.Errorf("molecule.yaml was not overwritten") } if !strings.Contains(string(data), "molecule CLI") { t.Errorf("molecule.yaml should have new scaffold content") } } func TestCLI_Completion_Help(t *testing.T) { exe := mol(t) root := repoRoot() for _, shell := range []string{"bash", "zsh", "fish", "powershell"} { t.Run(shell+"-help", func(t *testing.T) { cmd := exec.Command(exe, "completion", shell, "--help") var stdout, stderr bytes.Buffer cmd.Stdout = &stdout cmd.Stderr = &stderr cmd.Dir = root err := cmd.Run() if err != nil { t.Fatalf("molecule completion %s --help: %v\nstderr: %s", shell, err, stderr.String()) } out := stdout.String() if out == "" { t.Errorf("empty stdout for molecule completion %s --help", shell) } }) } } func TestCLI_Completion_GeneratesScript(t *testing.T) { exe := mol(t) root := repoRoot() for _, shell := range []string{"bash", "zsh", "fish", "powershell"} { t.Run(shell, func(t *testing.T) { cmd := exec.Command(exe, "completion", shell) var stdout, stderr bytes.Buffer cmd.Stdout = &stdout cmd.Stderr = &stderr cmd.Dir = root err := cmd.Run() if err != nil { t.Fatalf("molecule completion %s: %v\nstderr: %s", shell, err, stderr.String()) } out := stdout.String() if out == "" { t.Errorf("empty completion script for %s", shell) } // The script should mention molecule or contain a directive/completion call if !strings.Contains(out, "molecule") && !strings.Contains(out, "_molecule") { t.Errorf("completion script for %s does not mention molecule:\n%s", shell, out) } }) } } func TestCLI_Completion_InvalidShell(t *testing.T) { exe := mol(t) root := repoRoot() cmd := exec.Command(exe, "completion", "unsupported-shell") var stderr bytes.Buffer cmd.Stderr = &stderr cmd.Dir = root err := cmd.Run() if err == nil { t.Fatalf("expected error for unsupported shell, got none") } exitErr, ok := err.(*exec.ExitError) if !ok { t.Fatalf("expected *exec.ExitError, got %T", err) } if exitErr.ExitCode() == 0 { t.Errorf("expected non-zero exit code for unsupported shell, got 0") } }