fix: update OpenCode plugin integration
This commit is contained in:
1 parent
0234652963
commit
598b0bd2bb
3 files changed
+175
-45
No files matched your search
@@ -0,0 +1,138 @@
|
||||
package plugins
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func runOpenCodeModule(t *testing.T, modulePath string, scriptBody string) string {
|
||||
t.Helper()
|
||||
nodeBinary, err := exec.LookPath("node")
|
||||
if err != nil {
|
||||
t.Skip("Node.js is required to execute generated OpenCode plugins")
|
||||
}
|
||||
moduleURL := "file:///" + filepath.ToSlash(modulePath)
|
||||
moduleJSON, err := json.Marshal(moduleURL)
|
||||
if err != nil {
|
||||
t.Fatalf("marshal module URL: %v", err)
|
||||
}
|
||||
script := "const pluginModule = await import(" + string(moduleJSON) + ");\n" + scriptBody
|
||||
cmd := exec.Command(nodeBinary, "--input-type=module", "--eval", script)
|
||||
output, err := cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
t.Fatalf("execute generated plugin: %v\n%s", err, output)
|
||||
}
|
||||
return string(output)
|
||||
}
|
||||
|
||||
func writeOpenCodeModule(t *testing.T, hookPath string, guardPath string) string {
|
||||
t.Helper()
|
||||
modulePath := filepath.Join(t.TempDir(), "elydora-audit.mjs")
|
||||
if err := os.WriteFile(modulePath, []byte(buildOpenCodePlugin(hookPath, guardPath)), 0600); err != nil {
|
||||
t.Fatalf("write generated plugin: %v", err)
|
||||
}
|
||||
return modulePath
|
||||
}
|
||||
|
||||
func TestOpenCodePluginUsesCurrentAPIAndBlocksFrozenAgent(t *testing.T) {
|
||||
tempDir := t.TempDir()
|
||||
guardPath := filepath.Join(tempDir, "guard.cjs")
|
||||
if err := os.WriteFile(
|
||||
guardPath,
|
||||
[]byte("process.stderr.write('Agent is frozen by Elydora.'); process.exit(2);\n"),
|
||||
0600,
|
||||
); err != nil {
|
||||
t.Fatalf("write guard: %v", err)
|
||||
}
|
||||
modulePath := writeOpenCodeModule(t, filepath.Join(tempDir, "hook.cjs"), guardPath)
|
||||
|
||||
runOpenCodeModule(t, modulePath, `
|
||||
const hooks = await pluginModule.ElydoraAuditPlugin({ project: { name: 'project' } });
|
||||
if (typeof hooks['tool.execute.before'] !== 'function') process.exit(10);
|
||||
if (typeof hooks['tool.execute.after'] !== 'function') process.exit(11);
|
||||
try {
|
||||
await hooks['tool.execute.before'](
|
||||
{ tool: 'bash', sessionID: 'session-1', callID: 'call-1' },
|
||||
{ args: { command: 'echo test' } },
|
||||
);
|
||||
process.exit(12);
|
||||
} catch (error) {
|
||||
if (!String(error.message).includes('Agent is frozen by Elydora')) process.exit(13);
|
||||
}
|
||||
`)
|
||||
}
|
||||
|
||||
func TestOpenCodePluginForwardsToolEvent(t *testing.T) {
|
||||
tempDir := t.TempDir()
|
||||
capturePath := filepath.Join(tempDir, "captured-event.json")
|
||||
hookPath := filepath.Join(tempDir, "hook.cjs")
|
||||
captureJSON, err := json.Marshal(capturePath)
|
||||
if err != nil {
|
||||
t.Fatalf("marshal capture path: %v", err)
|
||||
}
|
||||
hookScript := `
|
||||
const fs = require('node:fs');
|
||||
const chunks = [];
|
||||
process.stdin.on('data', (chunk) => chunks.push(chunk));
|
||||
process.stdin.on('end', () => fs.writeFileSync(` + string(captureJSON) + `, Buffer.concat(chunks)));
|
||||
`
|
||||
if err := os.WriteFile(hookPath, []byte(hookScript), 0600); err != nil {
|
||||
t.Fatalf("write hook: %v", err)
|
||||
}
|
||||
modulePath := writeOpenCodeModule(t, hookPath, filepath.Join(tempDir, "guard.cjs"))
|
||||
|
||||
runOpenCodeModule(t, modulePath, `
|
||||
const hooks = await pluginModule.ElydoraAuditPlugin({ project: { name: 'project' } });
|
||||
await hooks['tool.execute.after'](
|
||||
{
|
||||
tool: 'bash',
|
||||
sessionID: 'session-1',
|
||||
callID: 'call-1',
|
||||
args: { command: 'echo test' },
|
||||
},
|
||||
{ title: 'Shell', output: 'test' },
|
||||
);
|
||||
`)
|
||||
|
||||
deadline := time.Now().Add(3 * time.Second)
|
||||
for time.Now().Before(deadline) {
|
||||
if _, err := os.Stat(capturePath); err == nil {
|
||||
break
|
||||
}
|
||||
time.Sleep(20 * time.Millisecond)
|
||||
}
|
||||
payload, err := os.ReadFile(capturePath)
|
||||
if err != nil {
|
||||
t.Fatalf("read captured event: %v", err)
|
||||
}
|
||||
var event map[string]any
|
||||
if err := json.Unmarshal(payload, &event); err != nil {
|
||||
t.Fatalf("decode captured event: %v", err)
|
||||
}
|
||||
if event["tool_name"] != "bash" || event["session_id"] != "session-1" {
|
||||
t.Fatalf("unexpected captured event: %s", payload)
|
||||
}
|
||||
toolInput, ok := event["tool_input"].(map[string]any)
|
||||
if !ok || toolInput["command"] != "echo test" {
|
||||
t.Fatalf("unexpected tool input: %v", event["tool_input"])
|
||||
}
|
||||
}
|
||||
|
||||
func TestOpenCodePluginUsesGlobalConfigDirectory(t *testing.T) {
|
||||
homeDir := t.TempDir()
|
||||
t.Setenv("HOME", homeDir)
|
||||
t.Setenv("USERPROFILE", homeDir)
|
||||
path, err := (&OpenCodePlugin{}).configPath()
|
||||
if err != nil {
|
||||
t.Fatalf("resolve config path: %v", err)
|
||||
}
|
||||
wantSuffix := filepath.Join(".config", "opencode", "plugins", "elydora-audit.mjs")
|
||||
if !strings.HasSuffix(path, wantSuffix) {
|
||||
t.Fatalf("config path = %q, want suffix %q", path, wantSuffix)
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user