Files
Elydora-Go-SDK/cmd/elydora/plugins/grok_test.go
T
雷电芽衣 301210ff25 docs(go): audit README and AGENTS
Align the README API reference with the exported surface, add the
upgrade note, document the shared modules and Linux test setup in
AGENTS.md, and scope the Grok expandable-path assertion to Windows.

Claude-Session: https://claude.ai/code/session_01Agoc9bRVE9KL7mmWtey9Vq
2026-09-03 02:43:55 -04:00

349 lines
13 KiB
Go

package plugins
import (
"os"
"path/filepath"
"reflect"
"runtime"
"strings"
"testing"
)
func TestGrokRegistryFactoryAndRuntimeOwnership(t *testing.T) {
entry := SupportedAgents[grokAgentKey]
if entry.Name != "Grok Build" || entry.ConfigDir != "~/.grok/hooks" ||
entry.ConfigFile != grokConfigFile {
t.Fatalf("Grok registry entry = %#v", entry)
}
plugin, ok := NewPlugin(grokAgentKey).(*GrokPlugin)
if !ok || !plugin.ManagesGuardRuntime() {
t.Fatalf("Grok plugin = %#v", NewPlugin(grokAgentKey))
}
}
func TestGrokCommandRoundTripAndRejectsInjection(t *testing.T) {
fixture := prepareGrokFixture(t, grokFixtureOptions{})
nodePath, err := resolveNodeRuntime()
if err != nil {
t.Fatalf("resolve Node.js runtime: %v", err)
}
command, err := buildEncodedCommand("Grok", nodePath, fixture.guardPath)
if err != nil {
t.Fatalf("build Grok command: %v", err)
}
executable, script, ok := parseGrokCommand(command)
if !ok || !sameManagedPath(executable, nodePath) ||
!sameManagedPath(script, fixture.guardPath) {
t.Fatalf("parsed Grok command = %q, %q, %v", executable, script, ok)
}
if runtime.GOOS == "windows" && strings.Contains(command, "%ELYDORA_HOOK_PATH%") {
t.Fatalf("Grok command exposes an expandable path: %q", command)
}
if _, err := buildEncodedCommand("Grok", "node", fixture.guardPath); err == nil {
t.Fatal("accepted a relative Grok runtime path")
}
if _, err := buildEncodedCommand("Grok", nodePath, "guard.js"); err == nil {
t.Fatal("accepted a relative Grok script path")
}
for _, invalid := range []string{
command + " --inspect",
command + "\nexit 0",
"node " + fixture.guardPath,
} {
if _, _, ok := parseGrokCommand(invalid); ok {
t.Fatalf("accepted non-canonical command %q", invalid)
}
}
}
func TestGrokInstallPreservesConfigAndIsIdempotent(t *testing.T) {
existing := `{
"schemaVersion": 1,
"hooks": {
"SessionStart": [{"hooks":[{"type":"http","url":"https://example.test/hook","timeout":0}],"label":"keep"}],
"PreToolUse": [{"matcher":"Bash|run_terminal_command","hooks":[{"type":"command","command":"existing-command","timeout":5,"env":{"A":"B"}}]}]
}
}`
fixture := prepareGrokFixture(t, grokFixtureOptions{existingRaw: &existing})
if err := fixture.plugin.Install(fixture.config); err != nil {
t.Fatalf("install Grok hooks: %v", err)
}
first, err := os.ReadFile(fixture.configPath)
if err != nil {
t.Fatalf("read first Grok config: %v", err)
}
if err := fixture.plugin.Install(fixture.config); err != nil {
t.Fatalf("repeat Grok install: %v", err)
}
second, err := os.ReadFile(fixture.configPath)
if err != nil || string(second) != string(first) {
t.Fatalf("repeat install changed config: %v", err)
}
settings := readGrokTestObject(t, fixture.configPath)
if settings["schemaVersion"] != float64(1) {
t.Fatalf("schema version changed: %#v", settings["schemaVersion"])
}
hooks := requireObject(t, settings["hooks"])
if len(requireArray(t, hooks["SessionStart"])) != 1 ||
len(requireArray(t, hooks["PreToolUse"])) != 2 {
t.Fatalf("user hooks changed: %#v", hooks)
}
requireStrictGrokTriple(t, settings)
for _, path := range []string{
filepath.Join(fixture.homeDir, ".grok", "hooks", grokConfigFile),
filepath.Join(fixture.homeDir, ".claude", "settings.json"),
filepath.Join(fixture.homeDir, ".cursor", "hooks.json"),
} {
if _, err := os.Lstat(path); !os.IsNotExist(err) {
t.Fatalf("compatibility config was written at %s: %v", path, err)
}
}
for _, path := range []string{
fixture.guardPath, fixture.hookPath, fixture.runtimeConfig, fixture.privateKey,
} {
if info, err := os.Lstat(path); err != nil || !info.Mode().IsRegular() {
t.Fatalf("managed runtime %s = %v, %v", path, info, err)
}
}
assertNoGrokTransactionArtifacts(t, fixture.homeDir)
}
func TestGrokEmptyHomeOverrideUsesDefault(t *testing.T) {
fixture := prepareGrokFixture(t, grokFixtureOptions{useDefaultHome: true})
if err := fixture.plugin.Install(fixture.config); err != nil {
t.Fatalf("install Grok hooks: %v", err)
}
status, err := fixture.plugin.Status()
if err != nil || !status.Installed || status.ConfigPath != fixture.configPath {
t.Fatalf("default-home status = %#v, %v", status, err)
}
}
func TestGrokMigratesLegacyCommandsAndPreservesLookalikes(t *testing.T) {
fixture := prepareGrokFixture(t, grokFixtureOptions{})
legacyGuard := legacyGrokCommand(t, fixture.guardPath)
legacyAudit := legacyGrokCommand(t, fixture.hookPath)
lookalike := legacyGuard + " --inspect"
existing := map[string]any{"hooks": map[string]any{
"PreToolUse": []any{
map[string]any{"hooks": []any{buildGrokHandler(legacyGuard)}},
map[string]any{"hooks": []any{buildGrokHandler(lookalike)}},
},
"PostToolUse": []any{
map[string]any{"hooks": []any{buildGrokHandler(legacyAudit)}},
},
}}
if err := os.MkdirAll(filepath.Dir(fixture.configPath), 0700); err != nil {
t.Fatalf("create Grok hooks directory: %v", err)
}
writeGrokTestObject(t, fixture.configPath, existing)
if err := fixture.plugin.Install(fixture.config); err != nil {
t.Fatalf("migrate Grok hooks: %v", err)
}
settings := readGrokTestObject(t, fixture.configPath)
requireStrictGrokTriple(t, settings)
raw, _ := os.ReadFile(fixture.configPath)
if !strings.Contains(string(raw), "--inspect") {
t.Fatalf("ownership lookalike was removed: %s", raw)
}
hooks := requireObject(t, settings["hooks"])
groups := requireArray(t, hooks["PreToolUse"])
managed := requireObject(t, groups[len(groups)-1])
managed["hooks"] = append(
requireArray(t, managed["hooks"]),
map[string]any{"type": "command", "command": "user-command", "timeout": float64(10)},
)
writeGrokTestObject(t, fixture.configPath, settings)
if err := fixture.plugin.Uninstall(grokTestAgentID); err != nil {
t.Fatalf("uninstall Grok hooks: %v", err)
}
remaining := readGrokTestObject(t, fixture.configPath)
remainingHooks := requireObject(t, remaining["hooks"])
if _, exists := remainingHooks["PostToolUse"]; exists {
t.Fatalf("managed success hook remains: %#v", remainingHooks)
}
if _, exists := remainingHooks["PostToolUseFailure"]; exists {
t.Fatalf("managed failure hook remains: %#v", remainingHooks)
}
commands := []string{}
for _, groupValue := range requireArray(t, remainingHooks["PreToolUse"]) {
for _, handlerValue := range requireArray(t, requireObject(t, groupValue)["hooks"]) {
commands = append(commands, requireObject(t, handlerValue)["command"].(string))
}
}
if !reflect.DeepEqual(commands, []string{lookalike, "user-command"}) {
t.Fatalf("remaining commands = %#v", commands)
}
}
func TestGrokUninstallPreservesUserConfigAndRemovesOwnedConfig(t *testing.T) {
userSource := `{"owner":"user","hooks":{"Notification":[]}}`
fixture := prepareGrokFixture(t, grokFixtureOptions{existingRaw: &userSource})
if err := fixture.plugin.Install(fixture.config); err != nil {
t.Fatalf("install user Grok hooks: %v", err)
}
if err := fixture.plugin.Uninstall(grokTestAgentID); err != nil {
t.Fatalf("uninstall user Grok hooks: %v", err)
}
remaining := readGrokTestObject(t, fixture.configPath)
if remaining["owner"] != "user" || !reflect.DeepEqual(
requireObject(t, remaining["hooks"])["Notification"],
[]any{},
) {
t.Fatalf("user config changed: %#v", remaining)
}
owned := prepareGrokFixture(t, grokFixtureOptions{})
if err := owned.plugin.Install(owned.config); err != nil {
t.Fatalf("install owned Grok hooks: %v", err)
}
if err := owned.plugin.Uninstall(grokTestAgentID); err != nil {
t.Fatalf("uninstall owned Grok hooks: %v", err)
}
if _, err := os.Lstat(owned.configPath); !os.IsNotExist(err) {
t.Fatalf("owned Grok config remains: %v", err)
}
}
func TestGrokStatusRequiresExactTripleIdentityAndRuntimeFiles(t *testing.T) {
fixture := prepareGrokFixture(t, grokFixtureOptions{})
if err := fixture.plugin.Install(fixture.config); err != nil {
t.Fatalf("install Grok hooks: %v", err)
}
assertInstalled := func(want bool) {
t.Helper()
status, err := fixture.plugin.Status()
if err != nil || status.Installed != want {
t.Fatalf("Grok status = %#v, %v; want installed %v", status, err, want)
}
}
assertInstalled(true)
settings := readGrokTestObject(t, fixture.configPath)
hooks := requireObject(t, settings["hooks"])
delete(hooks, "PostToolUseFailure")
writeGrokTestObject(t, fixture.configPath, settings)
assertInstalled(false)
if err := fixture.plugin.Install(fixture.config); err != nil {
t.Fatalf("repair Grok hooks: %v", err)
}
settings = readGrokTestObject(t, fixture.configPath)
hooks = requireObject(t, settings["hooks"])
hooks["PostToolUseFailure"] = append(
requireArray(t, hooks["PostToolUseFailure"]),
requireArray(t, hooks["PostToolUseFailure"])[0],
)
writeGrokTestObject(t, fixture.configPath, settings)
assertInstalled(false)
if err := fixture.plugin.Install(fixture.config); err != nil {
t.Fatalf("repair duplicate Grok hooks: %v", err)
}
if err := os.Remove(fixture.guardPath); err != nil {
t.Fatalf("remove guard runtime: %v", err)
}
status, err := fixture.plugin.Status()
if err != nil || status.Installed || !status.HookConfigured ||
status.HookScriptExists {
t.Fatalf("missing guard status = %#v, %v", status, err)
}
}
func TestGrokRejectsInvalidHookShapesBeforeRuntimeWrites(t *testing.T) {
for _, testCase := range []struct{ name, raw, want string }{
{"malformed", "{ malformed", "parse Grok user hooks"},
{"comments", "{\"hooks\":{} // comment\n}", "invalid character"},
{"trailing comma", `{"hooks":{},}`, "invalid character"},
{"non-finite timeout", `{"hooks":{"PreToolUse":[{"hooks":[{"type":"command","command":"x","timeout":NaN}]}]}}`, "invalid character"},
{"duplicate", `{"hooks":{},"hooks":{}}`, "duplicate"},
{"hooks", `{"hooks":null}`, `field "hooks" must be an object`},
{"event", `{"hooks":{"PreToolUse":null}}`, "must be an array"},
{"group", `{"hooks":{"PreToolUse":[null]}}`, "must be an object"},
{"matcher", `{"hooks":{"PreToolUse":[{"matcher":1,"hooks":[]}]}}`, "matcher must be a string"},
{"lifecycle matcher", `{"hooks":{"SessionStart":[{"matcher":"x","hooks":[]}]}}`, "cannot declare a matcher"},
{"handler", `{"hooks":{"PreToolUse":[{"hooks":[null]}]}}`, "must be an object"},
{"type", `{"hooks":{"PreToolUse":[{"hooks":[{"type":"file"}]}]}}`, "unsupported type"},
{"command", `{"hooks":{"PreToolUse":[{"hooks":[{"type":"command","command":""}]}]}}`, "non-empty command"},
{"http", `{"hooks":{"PostToolUse":[{"hooks":[{"type":"http","url":""}]}]}}`, "non-empty url"},
{"negative timeout", `{"hooks":{"PreToolUse":[{"hooks":[{"type":"command","command":"x","timeout":-1}]}]}}`, "non-negative integer"},
{"fraction timeout", `{"hooks":{"PreToolUse":[{"hooks":[{"type":"command","command":"x","timeout":1.5}]}]}}`, "non-negative integer"},
{"unsafe timeout", `{"hooks":{"PreToolUse":[{"hooks":[{"type":"command","command":"x","timeout":9007199254740992}]}]}}`, "non-negative integer"},
{"environment", `{"hooks":{"PreToolUse":[{"hooks":[{"type":"command","command":"x","env":{"A":1}}]}]}}`, "env must map names to strings"},
} {
t.Run(testCase.name, func(t *testing.T) {
fixture := prepareGrokFixture(
t,
grokFixtureOptions{existingRaw: grokString(testCase.raw)},
)
err := fixture.plugin.Install(fixture.config)
if err == nil || !strings.Contains(err.Error(), testCase.want) {
t.Fatalf("install error = %v, want %q", err, testCase.want)
}
raw, readErr := os.ReadFile(fixture.configPath)
if readErr != nil || string(raw) != testCase.raw {
t.Fatalf("original config changed: %q, %v", raw, readErr)
}
assertNoGrokRuntimeWrites(t, fixture)
})
}
}
func TestGrokRejectsRenderedConfigBeyondManagedSourceLimit(t *testing.T) {
prefix := `{"padding":"`
suffix := `"}`
padding := strings.Repeat(
"a",
maxManagedSourceBytes-len(prefix)-len(suffix),
)
raw := prefix + padding + suffix
fixture := prepareGrokFixture(t, grokFixtureOptions{existingRaw: &raw})
err := fixture.plugin.Install(fixture.config)
if err == nil || !strings.Contains(err.Error(), "exceeds") {
t.Fatalf("install error = %v", err)
}
current, readErr := os.ReadFile(fixture.configPath)
if readErr != nil || string(current) != raw {
t.Fatalf("large source changed: size=%d, err=%v", len(current), readErr)
}
assertNoGrokRuntimeWrites(t, fixture)
}
func TestGrokAcceptsOfficialZeroTimeoutAndStringEnvironment(t *testing.T) {
raw := `{"hooks":{"PreToolUse":[{"hooks":[{"type":"command","command":"user","timeout":0,"env":{"A":"B"}}]}]}}`
fixture := prepareGrokFixture(t, grokFixtureOptions{existingRaw: &raw})
if err := fixture.plugin.Install(fixture.config); err != nil {
t.Fatalf("install Grok hooks with official fields: %v", err)
}
hooks := requireObject(t, readGrokTestObject(t, fixture.configPath)["hooks"])
user := requireObject(t, requireArray(t, requireObject(
t,
requireArray(t, hooks["PreToolUse"])[0],
)["hooks"])[0])
if user["timeout"] != float64(0) {
t.Fatalf("zero timeout changed: %#v", user)
}
}
func TestGrokRuntimeConfigOmitsEmptyOptionalToken(t *testing.T) {
fixture := prepareGrokFixture(t, grokFixtureOptions{})
fixture.config.Token = ""
if err := fixture.plugin.Install(fixture.config); err != nil {
t.Fatalf("install Grok hooks without token: %v", err)
}
config := readGrokTestObject(t, fixture.runtimeConfig)
if _, exists := config["token"]; exists {
t.Fatalf("empty optional token was persisted: %#v", config)
}
}
func TestGrokWindowsAgentIDMatchingIsPlatformCorrect(t *testing.T) {
if runtime.GOOS == "windows" {
if !sameManagedAgentID("AGENT-1", "agent-1") {
t.Fatal("Windows Grok agent IDs should compare case-insensitively")
}
return
}
if sameManagedAgentID("AGENT-1", "agent-1") {
t.Fatal("POSIX Grok agent IDs should preserve case")
}
}