a06f331eb8
CI / benchmark (push) Has been skipped
install-script / posix-syntax (push) Successful in 6m1s
CI / build-onnx (push) Failing after 6m43s
init-smoke / dry-run (push) Failing after 15m57s
security / govulncheck (push) Has been cancelled
security / trivy-fs (push) Has been cancelled
CI / test (1.26, ubuntu-latest) (push) Has been cancelled
Scorecard supply-chain security / Scorecard analysis (push) Has been cancelled
CI / test (1.26, macos-latest) (push) Has been cancelled
CI / build-windows (push) Has been cancelled
CI / lint (push) Has been cancelled
install-script / powershell-syntax (push) Has been cancelled
install-script / install (macos-14) (push) Has been cancelled
install-script / install (ubuntu-latest) (push) Has been cancelled
174 lines
5.8 KiB
Go
174 lines
5.8 KiB
Go
package main
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/json"
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
// capturedAnalyze records the (repo, tool, args) the analyze verb lowered to.
|
|
type capturedAnalyze struct {
|
|
repo string
|
|
tool string
|
|
args map[string]any
|
|
}
|
|
|
|
// runAnalyzeCmd drives the real command tree (rootCmd → analyze) with the given
|
|
// argv, stubbing the daemon seam so the call never leaves the process. Returns
|
|
// the captured call (or nil if none was made), the combined out/err buffer, and
|
|
// any error from RunE.
|
|
func runAnalyzeCmd(t *testing.T, argv ...string) (*capturedAnalyze, *bytes.Buffer, error) {
|
|
t.Helper()
|
|
resetAnalyzeFlags()
|
|
|
|
orig := analyzeDaemonTool
|
|
t.Cleanup(func() { analyzeDaemonTool = orig })
|
|
|
|
var cap *capturedAnalyze
|
|
analyzeDaemonTool = func(repo, tool string, args map[string]any) (json.RawMessage, error) {
|
|
cap = &capturedAnalyze{repo: repo, tool: tool, args: args}
|
|
return json.RawMessage(`{"status":"ok"}`), nil
|
|
}
|
|
|
|
buf := &bytes.Buffer{}
|
|
rootCmd.SetOut(buf)
|
|
rootCmd.SetErr(buf)
|
|
rootCmd.SetIn(strings.NewReader(""))
|
|
rootCmd.SetArgs(append([]string{"analyze"}, argv...))
|
|
err := rootCmd.Execute()
|
|
return cap, buf, err
|
|
}
|
|
|
|
func resetAnalyzeFlags() {
|
|
resetCobraFlags(analyzeCmd)
|
|
analyzeIndex = "."
|
|
analyzeFormat = "json"
|
|
analyzeKind = ""
|
|
analyzeLimit = 0
|
|
analyzeCompact = false
|
|
analyzePathPrefix = ""
|
|
analyzeArgs = nil
|
|
}
|
|
|
|
// TestAnalyze_KindArgAndUniversalFlags asserts the kind, universal --limit, and
|
|
// a --arg coerced number all lower correctly onto the analyze tool.
|
|
func TestAnalyze_KindArgAndUniversalFlags(t *testing.T) {
|
|
cap, _, err := runAnalyzeCmd(t,
|
|
"--kind", "hotspots", "--arg", "threshold:=0.8", "--limit", "5")
|
|
require.NoError(t, err)
|
|
require.NotNil(t, cap)
|
|
require.Equal(t, "analyze", cap.tool)
|
|
require.Equal(t, "hotspots", cap.args["kind"])
|
|
require.Equal(t, 0.8, cap.args["threshold"]) // walrus raw-JSON number
|
|
require.Equal(t, 5, cap.args["limit"])
|
|
require.Equal(t, "json", cap.args["format"])
|
|
}
|
|
|
|
// TestAnalyze_PathPrefixAndCompact asserts the typed --path-prefix and --compact
|
|
// flags lower to path_prefix / compact.
|
|
func TestAnalyze_PathPrefixAndCompact(t *testing.T) {
|
|
cap, _, err := runAnalyzeCmd(t,
|
|
"--kind", "ownership", "--path-prefix", "internal/auth/", "--compact")
|
|
require.NoError(t, err)
|
|
require.Equal(t, "ownership", cap.args["kind"])
|
|
require.Equal(t, "internal/auth/", cap.args["path_prefix"])
|
|
require.Equal(t, true, cap.args["compact"])
|
|
}
|
|
|
|
// TestAnalyze_OnlyChangedTypedFlagsSent asserts unset universal flags are not
|
|
// forwarded, so the daemon's kind-specific defaults hold.
|
|
func TestAnalyze_OnlyChangedTypedFlagsSent(t *testing.T) {
|
|
cap, _, err := runAnalyzeCmd(t, "--kind", "dead_code")
|
|
require.NoError(t, err)
|
|
require.Equal(t, "dead_code", cap.args["kind"])
|
|
_, hasLimit := cap.args["limit"]
|
|
require.False(t, hasLimit, "unset --limit must not be forwarded")
|
|
_, hasCompact := cap.args["compact"]
|
|
require.False(t, hasCompact, "unset --compact must not be forwarded")
|
|
_, hasPrefix := cap.args["path_prefix"]
|
|
require.False(t, hasPrefix, "unset --path-prefix must not be forwarded")
|
|
}
|
|
|
|
// TestAnalyze_ArgOverridesTypedFlag asserts a --arg pair wins over the matching
|
|
// universal typed flag.
|
|
func TestAnalyze_ArgOverridesTypedFlag(t *testing.T) {
|
|
cap, _, err := runAnalyzeCmd(t,
|
|
"--kind", "cross_repo", "--limit", "10", "--arg", "limit=99")
|
|
require.NoError(t, err)
|
|
require.Equal(t, int64(99), cap.args["limit"], "--arg must override --limit")
|
|
}
|
|
|
|
// TestAnalyze_BogusKindClientSideError asserts an invalid --kind is rejected
|
|
// client-side (no daemon call) with a message listing valid kinds.
|
|
func TestAnalyze_BogusKindClientSideError(t *testing.T) {
|
|
called := false
|
|
orig := analyzeDaemonTool
|
|
t.Cleanup(func() { analyzeDaemonTool = orig })
|
|
resetAnalyzeFlags()
|
|
analyzeDaemonTool = func(string, string, map[string]any) (json.RawMessage, error) {
|
|
called = true
|
|
return nil, nil
|
|
}
|
|
|
|
buf := &bytes.Buffer{}
|
|
rootCmd.SetOut(buf)
|
|
rootCmd.SetErr(buf)
|
|
rootCmd.SetIn(strings.NewReader(""))
|
|
rootCmd.SetArgs([]string{"analyze", "--kind", "bogus"})
|
|
err := rootCmd.Execute()
|
|
require.Error(t, err)
|
|
require.False(t, called, "an invalid --kind must NOT call the daemon")
|
|
require.Contains(t, err.Error(), "unknown analyze kind")
|
|
require.Contains(t, err.Error(), "hotspots", "the error should list valid kinds")
|
|
}
|
|
|
|
// TestAnalyze_MissingKindError asserts --kind is required.
|
|
func TestAnalyze_MissingKindError(t *testing.T) {
|
|
_, _, err := runAnalyzeCmd(t)
|
|
require.Error(t, err)
|
|
require.Contains(t, err.Error(), "--kind is required")
|
|
}
|
|
|
|
// TestAnalyzeKinds_PrintsKindsNoDaemon asserts `analyze kinds` lists the kinds
|
|
// from the in-process SSOT without touching the daemon.
|
|
func TestAnalyzeKinds_PrintsKindsNoDaemon(t *testing.T) {
|
|
called := false
|
|
orig := analyzeDaemonTool
|
|
t.Cleanup(func() { analyzeDaemonTool = orig })
|
|
resetAnalyzeFlags()
|
|
analyzeDaemonTool = func(string, string, map[string]any) (json.RawMessage, error) {
|
|
called = true
|
|
return nil, nil
|
|
}
|
|
|
|
buf := &bytes.Buffer{}
|
|
rootCmd.SetOut(buf)
|
|
rootCmd.SetErr(buf)
|
|
rootCmd.SetIn(strings.NewReader(""))
|
|
rootCmd.SetArgs([]string{"analyze", "kinds"})
|
|
require.NoError(t, rootCmd.Execute())
|
|
require.False(t, called, "`analyze kinds` must not call the daemon")
|
|
|
|
out := buf.String()
|
|
for _, k := range []string{"hotspots", "dead_code", "cycles", "todos", "coverage_gaps"} {
|
|
require.Contains(t, out, k)
|
|
}
|
|
}
|
|
|
|
// TestAnalyze_DaemonRequired asserts the real call path returns the actionable
|
|
// daemon-required error when no daemon tracks the repo.
|
|
func TestAnalyze_DaemonRequired(t *testing.T) {
|
|
resetAnalyzeFlags()
|
|
buf := &bytes.Buffer{}
|
|
rootCmd.SetOut(buf)
|
|
rootCmd.SetErr(buf)
|
|
rootCmd.SetIn(strings.NewReader(""))
|
|
rootCmd.SetArgs([]string{"analyze", "--kind", "hotspots", "--index", t.TempDir()})
|
|
err := rootCmd.Execute()
|
|
require.Error(t, err)
|
|
require.Contains(t, err.Error(), "gortex track")
|
|
}
|