Files
zero/internal/cli/backends_test.go

156 lines
5.6 KiB
Go

package cli
import (
"bytes"
"context"
"encoding/json"
"errors"
"strings"
"testing"
"github.com/Gitlawb/zero/internal/config"
"github.com/Gitlawb/zero/internal/hooks"
"github.com/Gitlawb/zero/internal/mcp"
"github.com/Gitlawb/zero/internal/plugins"
"github.com/Gitlawb/zero/internal/tools"
"github.com/Gitlawb/zero/internal/zerocommands"
)
func TestRunBackendsJSONUsesLifecycleSnapshotWithoutConnectingMCP(t *testing.T) {
cwd := t.TempDir()
secret := "sk-proj-" + strings.Repeat("a", 24)
deps := appDeps{
getwd: func() (string, error) { return cwd, nil },
resolveMCPConfig: func(workspaceRoot string) (config.MCPConfig, error) {
if workspaceRoot != cwd {
t.Fatalf("workspaceRoot = %q, want %q", workspaceRoot, cwd)
}
return config.MCPConfig{Servers: map[string]config.MCPServerConfig{
"zulu": {
Type: "http",
URL: "https://admin:[email protected]/mcp?token=" + secret + "&mode=readonly",
Headers: map[string]string{
"Authorization": "Bearer " + secret,
},
},
"alpha": {
Type: "stdio",
Command: "alpha-mcp",
Args: []string{"--project", cwd},
Env: map[string]string{
"ALPHA_TOKEN": secret,
},
},
"disabled": {
Type: "stdio",
Command: "disabled-mcp",
Disabled: true,
},
}}, nil
},
loadHooks: func(options hooks.LoadOptions) (hooks.LoadResult, error) {
if options.Cwd != cwd {
t.Fatalf("hook Cwd = %q, want %q", options.Cwd, cwd)
}
return hooks.LoadResult{Config: hooks.Config{Hooks: []hooks.Definition{{
ID: "zero.preflight",
Event: hooks.EventBeforeTool,
Matcher: "bash",
Command: "sh",
Args: []string{"-c", "echo " + secret},
Enabled: true,
}}}}, nil
},
loadPlugins: func(options plugins.LoadOptions) (plugins.LoadResult, error) {
if options.Cwd != cwd {
t.Fatalf("plugin Cwd = %q, want %q", options.Cwd, cwd)
}
return plugins.LoadResult{Plugins: []plugins.LoadedPlugin{{
ID: "zero.docs",
Name: "Docs",
Description: "uses " + secret,
Enabled: true,
Source: plugins.SourceProject,
Root: "C:/tmp/" + secret,
PluginDir: "C:/tmp/plugin",
ManifestPath: "C:/tmp/plugin/plugin.json?token=" + secret,
Tools: []plugins.ToolExtension{{Name: "lookup"}},
Prompts: []plugins.PathExtension{{Name: "review"}},
Hooks: []plugins.HookExtension{{Name: "audit"}},
}}}, nil
},
registerMCPTools: func(context.Context, *tools.Registry, config.MCPConfig, mcp.RegisterOptions) (mcpToolRuntime, error) {
return nil, errors.New("zero backends must not connect to MCP servers")
},
}
var stdout bytes.Buffer
var stderr bytes.Buffer
exitCode := runWithDeps([]string{"backends", "--json"}, &stdout, &stderr, deps)
if exitCode != exitSuccess {
t.Fatalf("exitCode = %d stderr=%s", exitCode, stderr.String())
}
if strings.Contains(stdout.String(), secret) || strings.Contains(stdout.String(), "admin:secret") || strings.Contains(stdout.String(), "Authorization") || strings.Contains(stdout.String(), "ALPHA_TOKEN") {
t.Fatalf("backend JSON leaked secret material:\n%s", stdout.String())
}
var snapshot zerocommands.BackendLifecycleSnapshot
if err := json.Unmarshal(stdout.Bytes(), &snapshot); err != nil {
t.Fatalf("backend JSON failed to decode: %v\n%s", err, stdout.String())
}
if len(snapshot.MCPServers) != 2 || snapshot.MCPServers[0].Name != "alpha" || snapshot.MCPServers[1].Name != "zulu" {
t.Fatalf("unexpected MCP snapshots: %#v", snapshot.MCPServers)
}
if snapshot.MCPServers[0].ArgCount != 2 || snapshot.MCPServers[0].EnvKeyCount != 1 {
t.Fatalf("stdio MCP counts wrong: %#v", snapshot.MCPServers[0])
}
if snapshot.MCPServers[1].HeaderCount != 1 || !strings.Contains(snapshot.MCPServers[1].URL, "mode=readonly") {
t.Fatalf("http MCP snapshot missing safe status data: %#v", snapshot.MCPServers[1])
}
if len(snapshot.Hooks) != 1 || snapshot.Hooks[0].ID != "zero.preflight" || strings.Contains(strings.Join(snapshot.Hooks[0].Args, " "), secret) {
t.Fatalf("unexpected hook snapshots: %#v", snapshot.Hooks)
}
if len(snapshot.Plugins) != 1 || snapshot.Plugins[0].ID != "zero.docs" || snapshot.Plugins[0].ToolCount != 1 || snapshot.Plugins[0].PromptCount != 1 || snapshot.Plugins[0].HookCount != 1 {
t.Fatalf("unexpected plugin snapshots: %#v", snapshot.Plugins)
}
}
func TestRunBackendsTextAndHelp(t *testing.T) {
deps := appDeps{
getwd: func() (string, error) { return t.TempDir(), nil },
resolveMCPConfig: func(string) (config.MCPConfig, error) {
return config.MCPConfig{}, nil
},
loadHooks: func(hooks.LoadOptions) (hooks.LoadResult, error) {
return hooks.LoadResult{}, nil
},
loadPlugins: func(plugins.LoadOptions) (plugins.LoadResult, error) {
return plugins.LoadResult{}, nil
},
}
var stdout bytes.Buffer
var stderr bytes.Buffer
exitCode := runWithDeps([]string{"backends"}, &stdout, &stderr, deps)
if exitCode != exitSuccess {
t.Fatalf("exitCode = %d stderr=%s", exitCode, stderr.String())
}
for _, want := range []string{"Zero Backends:", "MCP servers: 0", "Hooks: 0", "Plugins: 0"} {
if !strings.Contains(stdout.String(), want) {
t.Fatalf("backend text missing %q:\n%s", want, stdout.String())
}
}
stdout.Reset()
stderr.Reset()
exitCode = runWithDeps([]string{"backends", "--help"}, &stdout, &stderr, appDeps{})
if exitCode != exitSuccess {
t.Fatalf("help exitCode = %d stderr=%s", exitCode, stderr.String())
}
for _, want := range []string{"Usage:", "zero backends", "--json"} {
if !strings.Contains(stdout.String(), want) {
t.Fatalf("backend help missing %q:\n%s", want, stdout.String())
}
}
}