Files
Elydora-Go-SDK/cmd/elydora/plugins/grok_test.go
T
雷电芽衣 34ff4c1ea8 chore(go): audit providers kimi grok qwen letta
Move Kimi, Grok, and Qwen onto managed_runtime.go and shell_command.go;
share the encoded and shell command builders and the quoted Windows
legacy parser with Droid; split managed_changes.go; dedupe Letta's hook
entry filtering and stop swallowing settings read errors.

Claude-Session: https://claude.ai/code/session_01Agoc9bRVE9KL7mmWtey9Vq
2026-09-03 02:16:58 -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 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")
}
}